github.com/jgadling/terraform@v0.3.8-0.20150227214559-abd68c2c87bc/terraform/context.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"sort"
     7  	"sync"
     8  
     9  	"github.com/hashicorp/go-multierror"
    10  	"github.com/hashicorp/terraform/config"
    11  	"github.com/hashicorp/terraform/config/module"
    12  )
    13  
    14  // InputMode defines what sort of input will be asked for when Input
    15  // is called on Context.
    16  type InputMode byte
    17  
    18  const (
    19  	// InputModeVar asks for variables
    20  	InputModeVar InputMode = 1 << iota
    21  
    22  	// InputModeProvider asks for provider variables
    23  	InputModeProvider
    24  
    25  	// InputModeStd is the standard operating mode and asks for both variables
    26  	// and providers.
    27  	InputModeStd = InputModeVar | InputModeProvider
    28  )
    29  
    30  // ContextOpts are the user-configurable options to create a context with
    31  // NewContext.
    32  type ContextOpts struct {
    33  	Diff         *Diff
    34  	Hooks        []Hook
    35  	Module       *module.Tree
    36  	Parallelism  int
    37  	State        *State
    38  	Providers    map[string]ResourceProviderFactory
    39  	Provisioners map[string]ResourceProvisionerFactory
    40  	Variables    map[string]string
    41  
    42  	UIInput UIInput
    43  }
    44  
    45  // Context represents all the context that Terraform needs in order to
    46  // perform operations on infrastructure. This structure is built using
    47  // NewContext. See the documentation for that.
    48  type Context struct {
    49  	diff         *Diff
    50  	diffLock     sync.RWMutex
    51  	hooks        []Hook
    52  	module       *module.Tree
    53  	providers    map[string]ResourceProviderFactory
    54  	provisioners map[string]ResourceProvisionerFactory
    55  	sh           *stopHook
    56  	state        *State
    57  	stateLock    sync.RWMutex
    58  	uiInput      UIInput
    59  	variables    map[string]string
    60  
    61  	l                   sync.Mutex // Lock acquired during any task
    62  	parallelSem         Semaphore
    63  	providerInputConfig map[string]map[string]interface{}
    64  	runCh               <-chan struct{}
    65  }
    66  
    67  // NewContext creates a new Context structure.
    68  //
    69  // Once a Context is creator, the pointer values within ContextOpts
    70  // should not be mutated in any way, since the pointers are copied, not
    71  // the values themselves.
    72  func NewContext(opts *ContextOpts) *Context {
    73  	// Copy all the hooks and add our stop hook. We don't append directly
    74  	// to the Config so that we're not modifying that in-place.
    75  	sh := new(stopHook)
    76  	hooks := make([]Hook, len(opts.Hooks)+1)
    77  	copy(hooks, opts.Hooks)
    78  	hooks[len(opts.Hooks)] = sh
    79  
    80  	state := opts.State
    81  	if state == nil {
    82  		state = new(State)
    83  		state.init()
    84  	}
    85  
    86  	// Determine parallelism, default to 10. We do this both to limit
    87  	// CPU pressure but also to have an extra guard against rate throttling
    88  	// from providers.
    89  	par := opts.Parallelism
    90  	if par == 0 {
    91  		par = 10
    92  	}
    93  
    94  	return &Context{
    95  		diff:         opts.Diff,
    96  		hooks:        hooks,
    97  		module:       opts.Module,
    98  		providers:    opts.Providers,
    99  		provisioners: opts.Provisioners,
   100  		state:        state,
   101  		uiInput:      opts.UIInput,
   102  		variables:    opts.Variables,
   103  
   104  		parallelSem:         NewSemaphore(par),
   105  		providerInputConfig: make(map[string]map[string]interface{}),
   106  		sh:                  sh,
   107  	}
   108  }
   109  
   110  // Graph returns the graph for this config.
   111  func (c *Context) Graph() (*Graph, error) {
   112  	return c.GraphBuilder().Build(RootModulePath)
   113  }
   114  
   115  // GraphBuilder returns the GraphBuilder that will be used to create
   116  // the graphs for this context.
   117  func (c *Context) GraphBuilder() GraphBuilder {
   118  	// TODO test
   119  	providers := make([]string, 0, len(c.providers))
   120  	for k, _ := range c.providers {
   121  		providers = append(providers, k)
   122  	}
   123  
   124  	provisioners := make([]string, 0, len(c.provisioners))
   125  	for k, _ := range c.provisioners {
   126  		provisioners = append(provisioners, k)
   127  	}
   128  
   129  	return &BuiltinGraphBuilder{
   130  		Root:         c.module,
   131  		Diff:         c.diff,
   132  		Providers:    providers,
   133  		Provisioners: provisioners,
   134  		State:        c.state,
   135  	}
   136  }
   137  
   138  // Input asks for input to fill variables and provider configurations.
   139  // This modifies the configuration in-place, so asking for Input twice
   140  // may result in different UI output showing different current values.
   141  func (c *Context) Input(mode InputMode) error {
   142  	v := c.acquireRun()
   143  	defer c.releaseRun(v)
   144  
   145  	if mode&InputModeVar != 0 {
   146  		// Walk the variables first for the root module. We walk them in
   147  		// alphabetical order for UX reasons.
   148  		rootConf := c.module.Config()
   149  		names := make([]string, len(rootConf.Variables))
   150  		m := make(map[string]*config.Variable)
   151  		for i, v := range rootConf.Variables {
   152  			names[i] = v.Name
   153  			m[v.Name] = v
   154  		}
   155  		sort.Strings(names)
   156  		for _, n := range names {
   157  			v := m[n]
   158  			switch v.Type() {
   159  			case config.VariableTypeMap:
   160  				continue
   161  			case config.VariableTypeString:
   162  				// Good!
   163  			default:
   164  				panic(fmt.Sprintf("Unknown variable type: %#v", v.Type()))
   165  			}
   166  
   167  			var defaultString string
   168  			if v.Default != nil {
   169  				defaultString = v.Default.(string)
   170  			}
   171  
   172  			// Ask the user for a value for this variable
   173  			var value string
   174  			for {
   175  				var err error
   176  				value, err = c.uiInput.Input(&InputOpts{
   177  					Id:          fmt.Sprintf("var.%s", n),
   178  					Query:       fmt.Sprintf("var.%s", n),
   179  					Default:     defaultString,
   180  					Description: v.Description,
   181  				})
   182  				if err != nil {
   183  					return fmt.Errorf(
   184  						"Error asking for %s: %s", n, err)
   185  				}
   186  
   187  				if value == "" && v.Required() {
   188  					// Redo if it is required.
   189  					continue
   190  				}
   191  
   192  				if value == "" {
   193  					// No value, just exit the loop. With no value, we just
   194  					// use whatever is currently set in variables.
   195  					break
   196  				}
   197  
   198  				break
   199  			}
   200  
   201  			if value != "" {
   202  				c.variables[n] = value
   203  			}
   204  		}
   205  	}
   206  
   207  	if mode&InputModeProvider != 0 {
   208  		// Do the walk
   209  		if _, err := c.walk(walkInput); err != nil {
   210  			return err
   211  		}
   212  	}
   213  
   214  	return nil
   215  }
   216  
   217  // Apply applies the changes represented by this context and returns
   218  // the resulting state.
   219  //
   220  // In addition to returning the resulting state, this context is updated
   221  // with the latest state.
   222  func (c *Context) Apply() (*State, error) {
   223  	v := c.acquireRun()
   224  	defer c.releaseRun(v)
   225  
   226  	// Copy our own state
   227  	c.state = c.state.DeepCopy()
   228  
   229  	// Do the walk
   230  	_, err := c.walk(walkApply)
   231  
   232  	// Clean out any unused things
   233  	c.state.prune()
   234  
   235  	return c.state, err
   236  }
   237  
   238  // Plan generates an execution plan for the given context.
   239  //
   240  // The execution plan encapsulates the context and can be stored
   241  // in order to reinstantiate a context later for Apply.
   242  //
   243  // Plan also updates the diff of this context to be the diff generated
   244  // by the plan, so Apply can be called after.
   245  func (c *Context) Plan(opts *PlanOpts) (*Plan, error) {
   246  	v := c.acquireRun()
   247  	defer c.releaseRun(v)
   248  
   249  	p := &Plan{
   250  		Module: c.module,
   251  		Vars:   c.variables,
   252  		State:  c.state,
   253  	}
   254  
   255  	var operation walkOperation
   256  	if opts != nil && opts.Destroy {
   257  		operation = walkPlanDestroy
   258  	} else {
   259  		// Set our state to be something temporary. We do this so that
   260  		// the plan can update a fake state so that variables work, then
   261  		// we replace it back with our old state.
   262  		old := c.state
   263  		if old == nil {
   264  			c.state = &State{}
   265  			c.state.init()
   266  		} else {
   267  			c.state = old.DeepCopy()
   268  		}
   269  		defer func() {
   270  			c.state = old
   271  		}()
   272  
   273  		operation = walkPlan
   274  	}
   275  
   276  	// Setup our diff
   277  	c.diffLock.Lock()
   278  	c.diff = new(Diff)
   279  	c.diff.init()
   280  	c.diffLock.Unlock()
   281  
   282  	// Do the walk
   283  	if _, err := c.walk(operation); err != nil {
   284  		return nil, err
   285  	}
   286  	p.Diff = c.diff
   287  
   288  	return p, nil
   289  }
   290  
   291  // Refresh goes through all the resources in the state and refreshes them
   292  // to their latest state. This will update the state that this context
   293  // works with, along with returning it.
   294  //
   295  // Even in the case an error is returned, the state will be returned and
   296  // will potentially be partially updated.
   297  func (c *Context) Refresh() (*State, error) {
   298  	v := c.acquireRun()
   299  	defer c.releaseRun(v)
   300  
   301  	// Copy our own state
   302  	c.state = c.state.DeepCopy()
   303  
   304  	// Do the walk
   305  	if _, err := c.walk(walkRefresh); err != nil {
   306  		return nil, err
   307  	}
   308  
   309  	// Clean out any unused things
   310  	c.state.prune()
   311  
   312  	return c.state, nil
   313  }
   314  
   315  // Stop stops the running task.
   316  //
   317  // Stop will block until the task completes.
   318  func (c *Context) Stop() {
   319  	c.l.Lock()
   320  	ch := c.runCh
   321  
   322  	// If we aren't running, then just return
   323  	if ch == nil {
   324  		c.l.Unlock()
   325  		return
   326  	}
   327  
   328  	// Tell the hook we want to stop
   329  	c.sh.Stop()
   330  
   331  	// Wait for us to stop
   332  	c.l.Unlock()
   333  	<-ch
   334  }
   335  
   336  // Validate validates the configuration and returns any warnings or errors.
   337  func (c *Context) Validate() ([]string, []error) {
   338  	v := c.acquireRun()
   339  	defer c.releaseRun(v)
   340  
   341  	var errs error
   342  
   343  	// Validate the configuration itself
   344  	if err := c.module.Validate(); err != nil {
   345  		errs = multierror.Append(errs, err)
   346  	}
   347  
   348  	// This only needs to be done for the root module, since inter-module
   349  	// variables are validated in the module tree.
   350  	if config := c.module.Config(); config != nil {
   351  		// Validate the user variables
   352  		if err := smcUserVariables(config, c.variables); len(err) > 0 {
   353  			errs = multierror.Append(errs, err...)
   354  		}
   355  	}
   356  
   357  	// Walk
   358  	walker, err := c.walk(walkValidate)
   359  	if err != nil {
   360  		return nil, multierror.Append(errs, err).Errors
   361  	}
   362  
   363  	// Return the result
   364  	rerrs := multierror.Append(errs, walker.ValidationErrors...)
   365  	return walker.ValidationWarnings, rerrs.Errors
   366  }
   367  
   368  func (c *Context) acquireRun() chan<- struct{} {
   369  	c.l.Lock()
   370  	defer c.l.Unlock()
   371  
   372  	// Wait for no channel to exist
   373  	for c.runCh != nil {
   374  		c.l.Unlock()
   375  		ch := c.runCh
   376  		<-ch
   377  		c.l.Lock()
   378  	}
   379  
   380  	ch := make(chan struct{})
   381  	c.runCh = ch
   382  	return ch
   383  }
   384  
   385  func (c *Context) releaseRun(ch chan<- struct{}) {
   386  	c.l.Lock()
   387  	defer c.l.Unlock()
   388  
   389  	close(ch)
   390  	c.runCh = nil
   391  	c.sh.Reset()
   392  }
   393  
   394  func (c *Context) walk(operation walkOperation) (*ContextGraphWalker, error) {
   395  	// Build the graph
   396  	graph, err := c.GraphBuilder().Build(RootModulePath)
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  
   401  	// Walk the graph
   402  	log.Printf("[INFO] Starting graph walk: %s", operation.String())
   403  	walker := &ContextGraphWalker{Context: c, Operation: operation}
   404  	return walker, graph.Walk(walker)
   405  }