github.com/profects/terraform@v0.9.0-beta1.0.20170227135739-92d4809db30d/backend/local/backend.go (about)

     1  package local
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  
     8  	"github.com/hashicorp/terraform/backend"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  	"github.com/hashicorp/terraform/state"
    11  	"github.com/hashicorp/terraform/terraform"
    12  	"github.com/mitchellh/cli"
    13  	"github.com/mitchellh/colorstring"
    14  )
    15  
    16  // Local is an implementation of EnhancedBackend that performs all operations
    17  // locally. This is the "default" backend and implements normal Terraform
    18  // behavior as it is well known.
    19  type Local struct {
    20  	// CLI and Colorize control the CLI output. If CLI is nil then no CLI
    21  	// output will be done. If CLIColor is nil then no coloring will be done.
    22  	CLI      cli.Ui
    23  	CLIColor *colorstring.Colorize
    24  
    25  	// StatePath is the local path where state is read from.
    26  	//
    27  	// StateOutPath is the local path where the state will be written.
    28  	// If this is empty, it will default to StatePath.
    29  	//
    30  	// StateBackupPath is the local path where a backup file will be written.
    31  	// If this is empty, no backup will be taken.
    32  	StatePath       string
    33  	StateOutPath    string
    34  	StateBackupPath string
    35  
    36  	// we only want to create a single instance of the local state
    37  	state state.State
    38  
    39  	// ContextOpts are the base context options to set when initializing a
    40  	// Terraform context. Many of these will be overridden or merged by
    41  	// Operation. See Operation for more details.
    42  	ContextOpts *terraform.ContextOpts
    43  
    44  	// OpInput will ask for necessary input prior to performing any operations.
    45  	//
    46  	// OpValidation will perform validation prior to running an operation. The
    47  	// variable naming doesn't match the style of others since we have a func
    48  	// Validate.
    49  	OpInput      bool
    50  	OpValidation bool
    51  
    52  	// Backend, if non-nil, will use this backend for non-enhanced behavior.
    53  	// This allows local behavior with remote state storage. It is a way to
    54  	// "upgrade" a non-enhanced backend to an enhanced backend with typical
    55  	// behavior.
    56  	//
    57  	// If this is nil, local performs normal state loading and storage.
    58  	Backend backend.Backend
    59  
    60  	schema *schema.Backend
    61  	opLock sync.Mutex
    62  	once   sync.Once
    63  }
    64  
    65  func (b *Local) Input(
    66  	ui terraform.UIInput, c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
    67  	b.once.Do(b.init)
    68  
    69  	f := b.schema.Input
    70  	if b.Backend != nil {
    71  		f = b.Backend.Input
    72  	}
    73  
    74  	return f(ui, c)
    75  }
    76  
    77  func (b *Local) Validate(c *terraform.ResourceConfig) ([]string, []error) {
    78  	b.once.Do(b.init)
    79  
    80  	f := b.schema.Validate
    81  	if b.Backend != nil {
    82  		f = b.Backend.Validate
    83  	}
    84  
    85  	return f(c)
    86  }
    87  
    88  func (b *Local) Configure(c *terraform.ResourceConfig) error {
    89  	b.once.Do(b.init)
    90  
    91  	f := b.schema.Configure
    92  	if b.Backend != nil {
    93  		f = b.Backend.Configure
    94  	}
    95  
    96  	return f(c)
    97  }
    98  
    99  func (b *Local) State() (state.State, error) {
   100  	// If we have a backend handling state, defer to that.
   101  	if b.Backend != nil {
   102  		return b.Backend.State()
   103  	}
   104  
   105  	if b.state != nil {
   106  		return b.state, nil
   107  	}
   108  
   109  	// Otherwise, we need to load the state.
   110  	var s state.State = &state.LocalState{
   111  		Path:    b.StatePath,
   112  		PathOut: b.StateOutPath,
   113  	}
   114  
   115  	// If we are backing up the state, wrap it
   116  	if path := b.StateBackupPath; path != "" {
   117  		s = &state.BackupState{
   118  			Real: s,
   119  			Path: path,
   120  		}
   121  	}
   122  
   123  	b.state = s
   124  	return s, nil
   125  }
   126  
   127  // Operation implements backend.Enhanced
   128  //
   129  // This will initialize an in-memory terraform.Context to perform the
   130  // operation within this process.
   131  //
   132  // The given operation parameter will be merged with the ContextOpts on
   133  // the structure with the following rules. If a rule isn't specified and the
   134  // name conflicts, assume that the field is overwritten if set.
   135  func (b *Local) Operation(ctx context.Context, op *backend.Operation) (*backend.RunningOperation, error) {
   136  	// Determine the function to call for our operation
   137  	var f func(context.Context, *backend.Operation, *backend.RunningOperation)
   138  	switch op.Type {
   139  	case backend.OperationTypeRefresh:
   140  		f = b.opRefresh
   141  	case backend.OperationTypePlan:
   142  		f = b.opPlan
   143  	case backend.OperationTypeApply:
   144  		f = b.opApply
   145  	default:
   146  		return nil, fmt.Errorf(
   147  			"Unsupported operation type: %s\n\n"+
   148  				"This is a bug in Terraform and should be reported. The local backend\n"+
   149  				"is built-in to Terraform and should always support all operations.",
   150  			op.Type)
   151  	}
   152  
   153  	// Lock
   154  	b.opLock.Lock()
   155  
   156  	// Build our running operation
   157  	runningCtx, runningCtxCancel := context.WithCancel(context.Background())
   158  	runningOp := &backend.RunningOperation{Context: runningCtx}
   159  
   160  	// Do it
   161  	go func() {
   162  		defer b.opLock.Unlock()
   163  		defer runningCtxCancel()
   164  		f(ctx, op, runningOp)
   165  	}()
   166  
   167  	// Return
   168  	return runningOp, nil
   169  }
   170  
   171  // Colorize returns the Colorize structure that can be used for colorizing
   172  // output. This is gauranteed to always return a non-nil value and so is useful
   173  // as a helper to wrap any potentially colored strings.
   174  func (b *Local) Colorize() *colorstring.Colorize {
   175  	if b.CLIColor != nil {
   176  		return b.CLIColor
   177  	}
   178  
   179  	return &colorstring.Colorize{
   180  		Colors:  colorstring.DefaultColors,
   181  		Disable: true,
   182  	}
   183  }
   184  
   185  func (b *Local) init() {
   186  	b.schema = &schema.Backend{
   187  		Schema: map[string]*schema.Schema{
   188  			"path": &schema.Schema{
   189  				Type:     schema.TypeString,
   190  				Optional: true,
   191  			},
   192  		},
   193  
   194  		ConfigureFunc: b.schemaConfigure,
   195  	}
   196  }
   197  
   198  func (b *Local) schemaConfigure(ctx context.Context) error {
   199  	d := schema.FromContextBackendConfig(ctx)
   200  
   201  	// Set the path if it is set
   202  	pathRaw, ok := d.GetOk("path")
   203  	if ok {
   204  		path := pathRaw.(string)
   205  		if path == "" {
   206  			return fmt.Errorf("configured path is empty")
   207  		}
   208  
   209  		b.StatePath = path
   210  	}
   211  
   212  	return nil
   213  }