2017-02-22 07:13:46 +01:00
package atlas
import (
"fmt"
"net/url"
"os"
"strings"
"sync"
2018-03-21 02:43:02 +01:00
"github.com/hashicorp/terraform/tfdiags"
"github.com/zclconf/go-cty/cty"
2017-02-22 07:13:46 +01:00
"github.com/hashicorp/terraform/backend"
2018-07-05 19:33:29 +02:00
"github.com/hashicorp/terraform/configs/configschema"
2017-02-22 07:13:46 +01:00
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/state/remote"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli"
"github.com/mitchellh/colorstring"
)
2018-03-21 02:43:02 +01:00
const EnvVarToken = "ATLAS_TOKEN"
const EnvVarAddress = "ATLAS_ADDRESS"
2017-02-22 07:13:46 +01:00
// Backend is an implementation of EnhancedBackend that performs all operations
// in Atlas. State must currently also be stored in Atlas, although it is worth
// investigating in the future if state storage can be external as well.
type Backend struct {
// CLI and Colorize control the CLI output. If CLI is nil then no CLI
// output will be done. If CLIColor is nil then no coloring will be done.
CLI cli . Ui
CLIColor * colorstring . Colorize
// ContextOpts are the base context options to set when initializing a
// Terraform context. Many of these will be overridden or merged by
// Operation. See Operation for more details.
ContextOpts * terraform . ContextOpts
//---------------------------------------------------------------
// Internal fields, do not set
//---------------------------------------------------------------
// stateClient is the legacy state client, setup in Configure
stateClient * stateClient
// schema is the schema for configuration, set by init
schema * schema . Backend
// opLock locks operations
opLock sync . Mutex
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
var _ backend . Backend = ( * Backend ) ( nil )
2018-10-31 16:45:03 +01:00
// New returns a new initialized Atlas backend.
func New ( ) * Backend {
return & Backend { }
}
2018-03-21 02:43:02 +01:00
func ( b * Backend ) ConfigSchema ( ) * configschema . Block {
return & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"name" : {
Type : cty . String ,
2017-02-22 07:13:46 +01:00
Required : true ,
2018-03-21 02:43:02 +01:00
Description : "Full name of the environment in Terraform Enterprise, such as 'myorg/myenv'" ,
2017-02-22 07:13:46 +01:00
} ,
2018-03-21 02:43:02 +01:00
"access_token" : {
Type : cty . String ,
Optional : true ,
Description : "Access token to use to access Terraform Enterprise; the ATLAS_TOKEN environment variable is used if this argument is not set" ,
2017-02-22 07:13:46 +01:00
} ,
2018-03-21 02:43:02 +01:00
"address" : {
Type : cty . String ,
2017-02-22 07:13:46 +01:00
Optional : true ,
2018-03-21 02:43:02 +01:00
Description : "Base URL for your Terraform Enterprise installation; the ATLAS_ADDRESS environment variable is used if this argument is not set, finally falling back to a default of 'https://atlas.hashicorp.com/' if neither are set." ,
2017-02-22 07:13:46 +01:00
} ,
} ,
}
}
2018-03-21 02:43:02 +01:00
func ( b * Backend ) ValidateConfig ( obj cty . Value ) tfdiags . Diagnostics {
var diags tfdiags . Diagnostics
name := obj . GetAttr ( "name" ) . AsString ( )
if ct := strings . Count ( name , "/" ) ; ct != 1 {
diags = diags . Append ( tfdiags . AttributeValue (
tfdiags . Error ,
"Invalid workspace selector" ,
` The "name" argument must be an organization name and a workspace name separated by a slash, such as "acme/network-production". ` ,
cty . Path { cty . GetAttrStep { Name : "name" } } ,
) )
2017-02-22 07:13:46 +01:00
}
2018-03-21 02:43:02 +01:00
if v := obj . GetAttr ( "address" ) ; ! v . IsNull ( ) {
addr := v . AsString ( )
_ , err := url . Parse ( addr )
if err != nil {
diags = diags . Append ( tfdiags . AttributeValue (
tfdiags . Error ,
"Invalid Terraform Enterprise URL" ,
fmt . Sprintf ( ` The "address" argument must be a valid URL: %s. ` , err ) ,
cty . Path { cty . GetAttrStep { Name : "address" } } ,
) )
}
2017-02-22 07:13:46 +01:00
}
2018-03-21 02:43:02 +01:00
return diags
}
func ( b * Backend ) Configure ( obj cty . Value ) tfdiags . Diagnostics {
var diags tfdiags . Diagnostics
2017-02-22 07:13:46 +01:00
2018-03-21 02:43:02 +01:00
client := & stateClient {
2017-02-22 07:13:46 +01:00
// This is optionally set during Atlas Terraform runs.
RunId : os . Getenv ( "ATLAS_RUN_ID" ) ,
}
2018-03-21 02:43:02 +01:00
name := obj . GetAttr ( "name" ) . AsString ( ) // assumed valid due to ValidateConfig method
slashIdx := strings . Index ( name , "/" )
client . User = name [ : slashIdx ]
client . Name = name [ slashIdx + 1 : ]
if v := obj . GetAttr ( "access_token" ) ; ! v . IsNull ( ) {
client . AccessToken = v . AsString ( )
} else {
client . AccessToken = os . Getenv ( EnvVarToken )
if client . AccessToken == "" {
diags = diags . Append ( tfdiags . AttributeValue (
tfdiags . Error ,
"Missing Terraform Enterprise access token" ,
` The "access_token" argument must be set unless the ATLAS_TOKEN environment variable is set to provide the authentication token for Terraform Enterprise. ` ,
cty . Path { cty . GetAttrStep { Name : "access_token" } } ,
) )
}
}
if v := obj . GetAttr ( "address" ) ; ! v . IsNull ( ) {
addr := v . AsString ( )
addrURL , err := url . Parse ( addr )
if err != nil {
// We already validated the URL in ValidateConfig, so this shouldn't happen
panic ( err )
}
client . Server = addr
client . ServerURL = addrURL
} else {
addr := os . Getenv ( EnvVarAddress )
if addr == "" {
addr = defaultAtlasServer
}
addrURL , err := url . Parse ( addr )
if err != nil {
diags = diags . Append ( tfdiags . AttributeValue (
tfdiags . Error ,
"Invalid Terraform Enterprise URL" ,
fmt . Sprintf ( ` The ATLAS_ADDRESS environment variable must contain a valid URL: %s. ` , err ) ,
cty . Path { cty . GetAttrStep { Name : "address" } } ,
) )
}
client . Server = addr
client . ServerURL = addrURL
}
b . stateClient = client
2017-02-22 07:13:46 +01:00
2018-03-21 02:43:02 +01:00
return diags
2018-07-04 12:11:35 +02:00
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
func ( b * Backend ) Workspaces ( ) ( [ ] string , error ) {
2018-10-31 16:45:03 +01:00
return nil , backend . ErrWorkspacesNotSupported
2018-07-04 12:11:35 +02:00
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
func ( b * Backend ) DeleteWorkspace ( name string ) error {
2018-10-31 16:45:03 +01:00
return backend . ErrWorkspacesNotSupported
2018-07-04 12:11:35 +02:00
}
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
func ( b * Backend ) StateMgr ( name string ) ( state . State , error ) {
2018-07-04 12:11:35 +02:00
if name != backend . DefaultStateName {
2018-10-31 16:45:03 +01:00
return nil , backend . ErrWorkspacesNotSupported
2018-07-04 12:11:35 +02:00
}
2018-03-21 02:43:02 +01:00
return & remote . State { Client : b . stateClient } , nil
2018-07-04 12:11:35 +02:00
}
// Colorize returns the Colorize structure that can be used for colorizing
// output. This is gauranteed to always return a non-nil value and so is useful
// as a helper to wrap any potentially colored strings.
func ( b * Backend ) Colorize ( ) * colorstring . Colorize {
if b . CLIColor != nil {
return b . CLIColor
}
return & colorstring . Colorize {
Colors : colorstring . DefaultColors ,
Disable : true ,
}
}