github.com/hooklift/terraform@v0.11.0-beta1.0.20171117000744-6786c1361ffe/backend/atlas/backend.go (about) 1 package atlas 2 3 import ( 4 "context" 5 "fmt" 6 "net/url" 7 "os" 8 "strings" 9 "sync" 10 11 "github.com/hashicorp/terraform/backend" 12 "github.com/hashicorp/terraform/helper/schema" 13 "github.com/hashicorp/terraform/state" 14 "github.com/hashicorp/terraform/state/remote" 15 "github.com/hashicorp/terraform/terraform" 16 "github.com/mitchellh/cli" 17 "github.com/mitchellh/colorstring" 18 ) 19 20 // Backend is an implementation of EnhancedBackend that performs all operations 21 // in Atlas. State must currently also be stored in Atlas, although it is worth 22 // investigating in the future if state storage can be external as well. 23 type Backend struct { 24 // CLI and Colorize control the CLI output. If CLI is nil then no CLI 25 // output will be done. If CLIColor is nil then no coloring will be done. 26 CLI cli.Ui 27 CLIColor *colorstring.Colorize 28 29 // ContextOpts are the base context options to set when initializing a 30 // Terraform context. Many of these will be overridden or merged by 31 // Operation. See Operation for more details. 32 ContextOpts *terraform.ContextOpts 33 34 //--------------------------------------------------------------- 35 // Internal fields, do not set 36 //--------------------------------------------------------------- 37 // stateClient is the legacy state client, setup in Configure 38 stateClient *stateClient 39 40 // schema is the schema for configuration, set by init 41 schema *schema.Backend 42 once sync.Once 43 44 // opLock locks operations 45 opLock sync.Mutex 46 } 47 48 func (b *Backend) Input( 49 ui terraform.UIInput, c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { 50 b.once.Do(b.init) 51 return b.schema.Input(ui, c) 52 } 53 54 func (b *Backend) Validate(c *terraform.ResourceConfig) ([]string, []error) { 55 b.once.Do(b.init) 56 return b.schema.Validate(c) 57 } 58 59 func (b *Backend) Configure(c *terraform.ResourceConfig) error { 60 b.once.Do(b.init) 61 return b.schema.Configure(c) 62 } 63 64 func (b *Backend) States() ([]string, error) { 65 return nil, backend.ErrNamedStatesNotSupported 66 } 67 68 func (b *Backend) DeleteState(name string) error { 69 return backend.ErrNamedStatesNotSupported 70 } 71 72 func (b *Backend) State(name string) (state.State, error) { 73 if name != backend.DefaultStateName { 74 return nil, backend.ErrNamedStatesNotSupported 75 } 76 77 return &remote.State{Client: b.stateClient}, nil 78 } 79 80 // Colorize returns the Colorize structure that can be used for colorizing 81 // output. This is gauranteed to always return a non-nil value and so is useful 82 // as a helper to wrap any potentially colored strings. 83 func (b *Backend) Colorize() *colorstring.Colorize { 84 if b.CLIColor != nil { 85 return b.CLIColor 86 } 87 88 return &colorstring.Colorize{ 89 Colors: colorstring.DefaultColors, 90 Disable: true, 91 } 92 } 93 94 func (b *Backend) init() { 95 b.schema = &schema.Backend{ 96 Schema: map[string]*schema.Schema{ 97 "name": &schema.Schema{ 98 Type: schema.TypeString, 99 Required: true, 100 Description: schemaDescriptions["name"], 101 }, 102 103 "access_token": &schema.Schema{ 104 Type: schema.TypeString, 105 Required: true, 106 Description: schemaDescriptions["access_token"], 107 DefaultFunc: schema.EnvDefaultFunc("ATLAS_TOKEN", nil), 108 }, 109 110 "address": &schema.Schema{ 111 Type: schema.TypeString, 112 Optional: true, 113 Description: schemaDescriptions["address"], 114 DefaultFunc: schema.EnvDefaultFunc("ATLAS_ADDRESS", defaultAtlasServer), 115 }, 116 }, 117 118 ConfigureFunc: b.schemaConfigure, 119 } 120 } 121 122 func (b *Backend) schemaConfigure(ctx context.Context) error { 123 d := schema.FromContextBackendConfig(ctx) 124 125 // Parse the address 126 addr := d.Get("address").(string) 127 addrUrl, err := url.Parse(addr) 128 if err != nil { 129 return fmt.Errorf("Error parsing 'address': %s", err) 130 } 131 132 // Parse the org/env 133 name := d.Get("name").(string) 134 parts := strings.Split(name, "/") 135 if len(parts) != 2 { 136 return fmt.Errorf("malformed name '%s', expected format '<org>/<name>'", name) 137 } 138 org := parts[0] 139 env := parts[1] 140 141 // Setup the client 142 b.stateClient = &stateClient{ 143 Server: addr, 144 ServerURL: addrUrl, 145 AccessToken: d.Get("access_token").(string), 146 User: org, 147 Name: env, 148 149 // This is optionally set during Atlas Terraform runs. 150 RunId: os.Getenv("ATLAS_RUN_ID"), 151 } 152 153 return nil 154 } 155 156 var schemaDescriptions = map[string]string{ 157 "name": "Full name of the environment in Atlas, such as 'hashicorp/myenv'", 158 "access_token": "Access token to use to access Atlas. If ATLAS_TOKEN is set then\n" + 159 "this will override any saved value for this.", 160 "address": "Address to your Atlas installation. This defaults to the publicly\n" + 161 "hosted version at 'https://atlas.hashicorp.com/'. This address\n" + 162 "should contain the full HTTP scheme to use.", 163 }