github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/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  }