2014-07-03 19:14:17 +02:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
2014-07-03 19:29:14 +02:00
|
|
|
"fmt"
|
|
|
|
"log"
|
2014-07-15 20:30:21 +02:00
|
|
|
"strconv"
|
2014-07-04 06:24:17 +02:00
|
|
|
"strings"
|
2014-07-03 19:29:14 +02:00
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
|
2014-07-03 19:14:17 +02:00
|
|
|
"github.com/hashicorp/terraform/config"
|
2014-09-23 00:37:29 +02:00
|
|
|
"github.com/hashicorp/terraform/config/module"
|
2014-07-03 19:29:14 +02:00
|
|
|
"github.com/hashicorp/terraform/depgraph"
|
2014-07-03 19:14:17 +02:00
|
|
|
"github.com/hashicorp/terraform/helper/multierror"
|
|
|
|
)
|
|
|
|
|
2014-07-03 20:28:04 +02:00
|
|
|
// This is a function type used to implement a walker for the resource
|
|
|
|
// tree internally on the Terraform structure.
|
2014-07-07 08:03:51 +02:00
|
|
|
type genericWalkFunc func(*Resource) error
|
2014-07-03 20:28:04 +02:00
|
|
|
|
2014-07-03 19:14:17 +02:00
|
|
|
// Context represents all the context that Terraform needs in order to
|
|
|
|
// perform operations on infrastructure. This structure is built using
|
|
|
|
// ContextOpts and NewContext. See the documentation for those.
|
|
|
|
//
|
|
|
|
// Additionally, a context can be created from a Plan using Plan.Context.
|
|
|
|
type Context struct {
|
2014-07-08 23:45:45 +02:00
|
|
|
config *config.Config
|
2014-09-23 00:37:29 +02:00
|
|
|
module *module.Tree
|
2014-07-08 23:45:45 +02:00
|
|
|
diff *Diff
|
|
|
|
hooks []Hook
|
|
|
|
state *State
|
|
|
|
providers map[string]ResourceProviderFactory
|
|
|
|
provisioners map[string]ResourceProvisionerFactory
|
|
|
|
variables map[string]string
|
2014-07-22 17:06:09 +02:00
|
|
|
defaultVars map[string]string
|
2014-07-03 20:27:30 +02:00
|
|
|
|
2014-07-08 20:42:03 +02:00
|
|
|
l sync.Mutex // Lock acquired during any task
|
|
|
|
parCh chan struct{} // Semaphore used to limit parallelism
|
|
|
|
sl sync.RWMutex // Lock acquired to R/W internal data
|
2014-07-03 20:27:30 +02:00
|
|
|
runCh <-chan struct{}
|
|
|
|
sh *stopHook
|
2014-07-03 19:14:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ContextOpts are the user-creatable configuration structure to create
|
|
|
|
// a context with NewContext.
|
|
|
|
type ContextOpts struct {
|
2014-07-08 23:45:45 +02:00
|
|
|
Diff *Diff
|
|
|
|
Hooks []Hook
|
2014-09-23 00:37:29 +02:00
|
|
|
Module *module.Tree
|
2014-07-08 23:45:45 +02:00
|
|
|
Parallelism int
|
|
|
|
State *State
|
|
|
|
Providers map[string]ResourceProviderFactory
|
|
|
|
Provisioners map[string]ResourceProvisionerFactory
|
|
|
|
Variables map[string]string
|
2014-07-03 19:14:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewContext creates a new context.
|
|
|
|
//
|
|
|
|
// Once a context is created, the pointer values within ContextOpts should
|
|
|
|
// not be mutated in any way, since the pointers are copied, not the values
|
|
|
|
// themselves.
|
|
|
|
func NewContext(opts *ContextOpts) *Context {
|
2014-07-03 20:27:30 +02:00
|
|
|
sh := new(stopHook)
|
|
|
|
|
|
|
|
// Copy all the hooks and add our stop hook. We don't append directly
|
|
|
|
// to the Config so that we're not modifying that in-place.
|
|
|
|
hooks := make([]Hook, len(opts.Hooks)+1)
|
|
|
|
copy(hooks, opts.Hooks)
|
|
|
|
hooks[len(opts.Hooks)] = sh
|
|
|
|
|
2014-07-08 20:42:03 +02:00
|
|
|
// Make the parallelism channel
|
|
|
|
par := opts.Parallelism
|
|
|
|
if par == 0 {
|
|
|
|
par = 10
|
|
|
|
}
|
|
|
|
parCh := make(chan struct{}, par)
|
|
|
|
|
2014-09-23 00:37:29 +02:00
|
|
|
var config *config.Config
|
|
|
|
if opts.Module != nil {
|
|
|
|
config = opts.Module.Config()
|
|
|
|
}
|
|
|
|
|
2014-07-22 17:06:09 +02:00
|
|
|
// Calculate all the default variables
|
|
|
|
defaultVars := make(map[string]string)
|
2014-09-23 00:37:29 +02:00
|
|
|
if config != nil {
|
|
|
|
for _, v := range config.Variables {
|
2014-08-19 18:05:19 +02:00
|
|
|
for k, val := range v.DefaultsMap() {
|
|
|
|
defaultVars[k] = val
|
|
|
|
}
|
2014-07-22 17:06:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-03 19:14:17 +02:00
|
|
|
return &Context{
|
2014-09-23 00:37:29 +02:00
|
|
|
config: config,
|
2014-07-08 23:45:45 +02:00
|
|
|
diff: opts.Diff,
|
|
|
|
hooks: hooks,
|
2014-09-23 00:37:29 +02:00
|
|
|
module: opts.Module,
|
2014-07-08 23:45:45 +02:00
|
|
|
state: opts.State,
|
|
|
|
providers: opts.Providers,
|
|
|
|
provisioners: opts.Provisioners,
|
|
|
|
variables: opts.Variables,
|
2014-07-22 17:06:09 +02:00
|
|
|
defaultVars: defaultVars,
|
2014-07-03 20:27:30 +02:00
|
|
|
|
2014-07-08 20:42:03 +02:00
|
|
|
parCh: parCh,
|
|
|
|
sh: sh,
|
2014-07-03 19:14:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-03 20:04:04 +02:00
|
|
|
// Apply applies the changes represented by this context and returns
|
|
|
|
// the resulting state.
|
|
|
|
//
|
|
|
|
// In addition to returning the resulting state, this context is updated
|
|
|
|
// with the latest state.
|
|
|
|
func (c *Context) Apply() (*State, error) {
|
2014-07-03 20:27:30 +02:00
|
|
|
v := c.acquireRun()
|
|
|
|
defer c.releaseRun(v)
|
|
|
|
|
2014-07-03 20:04:04 +02:00
|
|
|
g, err := Graph(&GraphOpts{
|
2014-07-08 23:45:45 +02:00
|
|
|
Diff: c.diff,
|
2014-09-23 00:37:29 +02:00
|
|
|
Module: c.module,
|
2014-07-08 23:45:45 +02:00
|
|
|
Providers: c.providers,
|
|
|
|
Provisioners: c.provisioners,
|
|
|
|
State: c.state,
|
2014-07-03 20:04:04 +02:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2014-07-07 08:03:51 +02:00
|
|
|
// Set our state right away. No matter what, this IS our new state,
|
|
|
|
// even if there is an error below.
|
|
|
|
c.state = c.state.deepcopy()
|
2014-09-17 01:21:09 +02:00
|
|
|
if c.state == nil {
|
|
|
|
c.state = &State{}
|
|
|
|
}
|
|
|
|
c.state.init()
|
2014-07-03 20:04:04 +02:00
|
|
|
|
2014-09-22 07:08:21 +02:00
|
|
|
// Initialize the state with all the resources
|
|
|
|
graphInitState(c.state, g)
|
|
|
|
|
2014-07-03 20:04:04 +02:00
|
|
|
// Walk
|
2014-07-24 16:20:59 +02:00
|
|
|
log.Printf("[INFO] Apply walk starting")
|
2014-09-23 22:21:45 +02:00
|
|
|
wc := c.walkContext(rootModulePath)
|
|
|
|
err = g.Walk(wc.applyWalkFn())
|
2014-07-24 16:20:59 +02:00
|
|
|
log.Printf("[INFO] Apply walk complete")
|
2014-07-03 20:04:04 +02:00
|
|
|
|
2014-07-13 20:07:31 +02:00
|
|
|
// Prune the state so that we have as clean a state as possible
|
|
|
|
c.state.prune()
|
|
|
|
|
2014-07-05 00:36:28 +02:00
|
|
|
// If we have no errors, then calculate the outputs if we have any
|
2014-09-18 02:35:43 +02:00
|
|
|
root := c.state.RootModule()
|
|
|
|
if err == nil && len(c.config.Outputs) > 0 && len(root.Resources) > 0 {
|
2014-09-16 02:11:36 +02:00
|
|
|
outputs := make(map[string]string)
|
2014-07-05 00:36:28 +02:00
|
|
|
for _, o := range c.config.Outputs {
|
2014-09-23 22:21:45 +02:00
|
|
|
if err = wc.computeVars(o.RawConfig); err != nil {
|
2014-07-05 00:36:28 +02:00
|
|
|
break
|
|
|
|
}
|
2014-09-16 02:11:36 +02:00
|
|
|
outputs[o.Name] = o.RawConfig.Config()["value"].(string)
|
2014-07-05 00:36:28 +02:00
|
|
|
}
|
2014-09-16 02:11:36 +02:00
|
|
|
|
|
|
|
// Assign the outputs to the root module
|
2014-09-18 02:35:43 +02:00
|
|
|
root.Outputs = outputs
|
2014-07-05 00:36:28 +02:00
|
|
|
}
|
|
|
|
|
2014-07-07 08:03:51 +02:00
|
|
|
return c.state, err
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
|
|
|
|
2014-07-13 04:23:56 +02:00
|
|
|
// Graph returns the graph for this context.
|
|
|
|
func (c *Context) Graph() (*depgraph.Graph, error) {
|
|
|
|
return c.graph()
|
|
|
|
}
|
|
|
|
|
2014-07-03 19:44:30 +02:00
|
|
|
// Plan generates an execution plan for the given context.
|
|
|
|
//
|
|
|
|
// The execution plan encapsulates the context and can be stored
|
|
|
|
// in order to reinstantiate a context later for Apply.
|
2014-07-03 20:04:04 +02:00
|
|
|
//
|
|
|
|
// Plan also updates the diff of this context to be the diff generated
|
|
|
|
// by the plan, so Apply can be called after.
|
2014-07-03 19:44:30 +02:00
|
|
|
func (c *Context) Plan(opts *PlanOpts) (*Plan, error) {
|
2014-07-03 20:28:47 +02:00
|
|
|
v := c.acquireRun()
|
|
|
|
defer c.releaseRun(v)
|
|
|
|
|
2014-07-03 19:44:30 +02:00
|
|
|
g, err := Graph(&GraphOpts{
|
2014-09-23 00:37:29 +02:00
|
|
|
Module: c.module,
|
2014-07-08 23:45:45 +02:00
|
|
|
Providers: c.providers,
|
|
|
|
Provisioners: c.provisioners,
|
|
|
|
State: c.state,
|
2014-07-03 19:44:30 +02:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
p := &Plan{
|
|
|
|
Config: c.config,
|
|
|
|
Vars: c.variables,
|
|
|
|
State: c.state,
|
|
|
|
}
|
2014-07-07 08:03:51 +02:00
|
|
|
|
|
|
|
var walkFn depgraph.WalkFunc
|
|
|
|
|
|
|
|
if opts != nil && opts.Destroy {
|
|
|
|
// If we're destroying, we use a different walk function since it
|
|
|
|
// doesn't need as many details.
|
|
|
|
walkFn = c.planDestroyWalkFn(p)
|
|
|
|
} else {
|
|
|
|
// Set our state to be something temporary. We do this so that
|
|
|
|
// the plan can update a fake state so that variables work, then
|
|
|
|
// we replace it back with our old state.
|
|
|
|
old := c.state
|
2014-09-17 01:21:09 +02:00
|
|
|
if old == nil {
|
|
|
|
c.state = &State{}
|
|
|
|
c.state.init()
|
|
|
|
} else {
|
|
|
|
c.state = old.deepcopy()
|
|
|
|
}
|
2014-07-07 08:03:51 +02:00
|
|
|
defer func() {
|
|
|
|
c.state = old
|
|
|
|
}()
|
|
|
|
|
2014-09-22 07:08:21 +02:00
|
|
|
// Initialize the state with all the resources
|
|
|
|
graphInitState(c.state, g)
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
walkFn = c.walkContext(rootModulePath).planWalkFn(p)
|
2014-07-07 08:03:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Walk and run the plan
|
|
|
|
err = g.Walk(walkFn)
|
2014-07-03 20:04:04 +02:00
|
|
|
|
|
|
|
// Update the diff so that our context is up-to-date
|
|
|
|
c.diff = p.Diff
|
|
|
|
|
2014-07-03 19:44:30 +02:00
|
|
|
return p, err
|
|
|
|
}
|
|
|
|
|
2014-07-03 19:29:14 +02:00
|
|
|
// Refresh goes through all the resources in the state and refreshes them
|
|
|
|
// to their latest state. This will update the state that this context
|
|
|
|
// works with, along with returning it.
|
|
|
|
//
|
|
|
|
// Even in the case an error is returned, the state will be returned and
|
|
|
|
// will potentially be partially updated.
|
|
|
|
func (c *Context) Refresh() (*State, error) {
|
2014-07-03 20:28:47 +02:00
|
|
|
v := c.acquireRun()
|
|
|
|
defer c.releaseRun(v)
|
|
|
|
|
2014-09-20 01:24:17 +02:00
|
|
|
// Update our state
|
|
|
|
c.state = c.state.deepcopy()
|
|
|
|
|
2014-07-03 19:29:14 +02:00
|
|
|
g, err := Graph(&GraphOpts{
|
2014-09-23 00:37:29 +02:00
|
|
|
Module: c.module,
|
2014-07-08 23:45:45 +02:00
|
|
|
Providers: c.providers,
|
|
|
|
Provisioners: c.provisioners,
|
|
|
|
State: c.state,
|
2014-07-03 19:29:14 +02:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return c.state, err
|
|
|
|
}
|
|
|
|
|
2014-09-22 07:08:21 +02:00
|
|
|
if c.state != nil {
|
|
|
|
// Initialize the state with all the resources
|
|
|
|
graphInitState(c.state, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk the graph
|
2014-09-23 22:21:45 +02:00
|
|
|
err = g.Walk(c.walkContext(rootModulePath).refreshWalkFn())
|
2014-09-20 01:24:17 +02:00
|
|
|
|
|
|
|
// Prune the state
|
|
|
|
c.state.prune()
|
2014-07-08 01:22:09 +02:00
|
|
|
return c.state, err
|
2014-07-03 19:29:14 +02:00
|
|
|
}
|
|
|
|
|
2014-07-03 20:27:30 +02:00
|
|
|
// Stop stops the running task.
|
|
|
|
//
|
|
|
|
// Stop will block until the task completes.
|
|
|
|
func (c *Context) Stop() {
|
|
|
|
c.l.Lock()
|
|
|
|
ch := c.runCh
|
|
|
|
|
|
|
|
// If we aren't running, then just return
|
|
|
|
if ch == nil {
|
|
|
|
c.l.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell the hook we want to stop
|
|
|
|
c.sh.Stop()
|
|
|
|
|
|
|
|
// Wait for us to stop
|
|
|
|
c.l.Unlock()
|
|
|
|
<-ch
|
|
|
|
}
|
|
|
|
|
2014-07-03 19:14:17 +02:00
|
|
|
// Validate validates the configuration and returns any warnings or errors.
|
|
|
|
func (c *Context) Validate() ([]string, []error) {
|
|
|
|
var rerr *multierror.Error
|
|
|
|
|
|
|
|
// Validate the configuration itself
|
|
|
|
if err := c.config.Validate(); err != nil {
|
|
|
|
rerr = multierror.ErrorAppend(rerr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate the user variables
|
|
|
|
if errs := smcUserVariables(c.config, c.variables); len(errs) > 0 {
|
|
|
|
rerr = multierror.ErrorAppend(rerr, errs...)
|
|
|
|
}
|
|
|
|
|
2014-07-03 21:17:56 +02:00
|
|
|
// Validate the graph
|
|
|
|
g, err := c.graph()
|
|
|
|
if err != nil {
|
|
|
|
rerr = multierror.ErrorAppend(rerr, fmt.Errorf(
|
|
|
|
"Error creating graph: %s", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk the graph and validate all the configs
|
|
|
|
var warns []string
|
2014-07-03 19:14:17 +02:00
|
|
|
var errs []error
|
2014-07-08 00:21:46 +02:00
|
|
|
if g != nil {
|
|
|
|
err = g.Walk(c.validateWalkFn(&warns, &errs))
|
|
|
|
if err != nil {
|
|
|
|
rerr = multierror.ErrorAppend(rerr, fmt.Errorf(
|
|
|
|
"Error validating resources in graph: %s", err))
|
|
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
|
|
rerr = multierror.ErrorAppend(rerr, errs...)
|
|
|
|
}
|
2014-07-03 21:17:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
errs = nil
|
2014-07-03 19:14:17 +02:00
|
|
|
if rerr != nil && len(rerr.Errors) > 0 {
|
|
|
|
errs = rerr.Errors
|
|
|
|
}
|
|
|
|
|
2014-07-03 21:17:56 +02:00
|
|
|
return warns, errs
|
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *Context) graph() (*depgraph.Graph, error) {
|
|
|
|
return Graph(&GraphOpts{
|
|
|
|
Diff: c.diff,
|
|
|
|
Module: c.module,
|
|
|
|
Providers: c.providers,
|
|
|
|
Provisioners: c.provisioners,
|
|
|
|
State: c.state,
|
|
|
|
})
|
|
|
|
}
|
2014-07-05 00:36:28 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *Context) acquireRun() chan<- struct{} {
|
|
|
|
c.l.Lock()
|
|
|
|
defer c.l.Unlock()
|
2014-07-22 17:18:53 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
// Wait for no channel to exist
|
|
|
|
for c.runCh != nil {
|
|
|
|
c.l.Unlock()
|
|
|
|
ch := c.runCh
|
|
|
|
<-ch
|
|
|
|
c.l.Lock()
|
2014-07-05 00:36:28 +02:00
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
ch := make(chan struct{})
|
|
|
|
c.runCh = ch
|
|
|
|
return ch
|
2014-07-05 00:36:28 +02:00
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *Context) releaseRun(ch chan<- struct{}) {
|
|
|
|
c.l.Lock()
|
|
|
|
defer c.l.Unlock()
|
2014-07-06 23:09:44 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
close(ch)
|
|
|
|
c.runCh = nil
|
|
|
|
c.sh.Reset()
|
|
|
|
}
|
2014-07-07 08:03:51 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *Context) planDestroyWalkFn(result *Plan) depgraph.WalkFunc {
|
|
|
|
var l sync.Mutex
|
2014-09-16 02:30:18 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
// Initialize the result
|
|
|
|
result.init()
|
2014-07-05 19:48:47 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
return func(n *depgraph.Noun) error {
|
|
|
|
rn, ok := n.Meta.(*GraphNodeResource)
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
2014-09-17 01:55:10 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
r := rn.Resource
|
|
|
|
if r.State != nil && r.State.ID != "" {
|
|
|
|
log.Printf("[DEBUG] %s: Making for destroy", r.Id)
|
2014-08-31 02:25:34 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
l.Lock()
|
|
|
|
defer l.Unlock()
|
|
|
|
result.Diff.RootModule().Resources[r.Id] = &InstanceDiff{Destroy: true}
|
|
|
|
} else {
|
|
|
|
log.Printf("[DEBUG] %s: Not marking for destroy, no ID", r.Id)
|
2014-08-31 02:25:34 +02:00
|
|
|
}
|
2014-07-05 19:48:47 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
return nil
|
|
|
|
}
|
2014-07-05 19:48:47 +02:00
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *Context) validateWalkFn(rws *[]string, res *[]error) depgraph.WalkFunc {
|
|
|
|
var l sync.Mutex
|
2014-07-07 08:03:51 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
return func(n *depgraph.Noun) error {
|
|
|
|
// If it is the root node, ignore
|
|
|
|
if n.Name == GraphRootNode {
|
|
|
|
return nil
|
2014-07-05 19:48:47 +02:00
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
switch rn := n.Meta.(type) {
|
|
|
|
case *GraphNodeResource:
|
|
|
|
if rn.Resource == nil {
|
|
|
|
panic("resource should never be nil")
|
|
|
|
}
|
2014-09-16 02:30:18 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
// If it doesn't have a provider, that is a different problem
|
|
|
|
if rn.Resource.Provider == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2014-08-05 19:12:35 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
// Don't validate orphans since they never have a config
|
|
|
|
if rn.Resource.Flags&FlagOrphan != 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2014-08-05 19:12:35 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
log.Printf("[INFO] Validating resource: %s", rn.Resource.Id)
|
|
|
|
ws, es := rn.Resource.Provider.ValidateResource(
|
|
|
|
rn.Resource.Info.Type, rn.Resource.Config)
|
|
|
|
for i, w := range ws {
|
|
|
|
ws[i] = fmt.Sprintf("'%s' warning: %s", rn.Resource.Id, w)
|
|
|
|
}
|
|
|
|
for i, e := range es {
|
|
|
|
es[i] = fmt.Errorf("'%s' error: %s", rn.Resource.Id, e)
|
|
|
|
}
|
2014-07-05 19:48:47 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
l.Lock()
|
|
|
|
*rws = append(*rws, ws...)
|
|
|
|
*res = append(*res, es...)
|
|
|
|
l.Unlock()
|
2014-09-17 01:55:10 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
for idx, p := range rn.Resource.Provisioners {
|
|
|
|
ws, es := p.Provisioner.Validate(p.Config)
|
|
|
|
for i, w := range ws {
|
|
|
|
ws[i] = fmt.Sprintf("'%s.provisioner.%d' warning: %s", rn.Resource.Id, idx, w)
|
|
|
|
}
|
|
|
|
for i, e := range es {
|
|
|
|
es[i] = fmt.Errorf("'%s.provisioner.%d' error: %s", rn.Resource.Id, idx, e)
|
|
|
|
}
|
2014-07-05 19:48:47 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
l.Lock()
|
|
|
|
*rws = append(*rws, ws...)
|
|
|
|
*res = append(*res, es...)
|
|
|
|
l.Unlock()
|
|
|
|
}
|
2014-07-05 19:48:47 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
case *GraphNodeResourceProvider:
|
|
|
|
var raw *config.RawConfig
|
|
|
|
if rn.Config != nil {
|
|
|
|
raw = rn.Config.RawConfig
|
|
|
|
}
|
2014-07-05 19:48:47 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
rc := NewResourceConfig(raw)
|
2014-07-05 19:48:47 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
for k, p := range rn.Providers {
|
|
|
|
log.Printf("[INFO] Validating provider: %s", k)
|
|
|
|
ws, es := p.Validate(rc)
|
|
|
|
for i, w := range ws {
|
|
|
|
ws[i] = fmt.Sprintf("Provider '%s' warning: %s", k, w)
|
|
|
|
}
|
|
|
|
for i, e := range es {
|
|
|
|
es[i] = fmt.Errorf("Provider '%s' error: %s", k, e)
|
|
|
|
}
|
2014-07-03 19:29:14 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
l.Lock()
|
|
|
|
*rws = append(*rws, ws...)
|
|
|
|
*res = append(*res, es...)
|
|
|
|
l.Unlock()
|
|
|
|
}
|
|
|
|
}
|
2014-07-03 20:27:30 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
return nil
|
2014-07-03 20:27:30 +02:00
|
|
|
}
|
2014-09-23 22:21:45 +02:00
|
|
|
}
|
2014-07-03 20:27:30 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *Context) walkContext(path []string) *walkContext {
|
|
|
|
return &walkContext{
|
|
|
|
Context: c,
|
|
|
|
Path: path,
|
|
|
|
}
|
2014-07-03 20:27:30 +02:00
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
// walkContext is the context in which a graph walk is done. It stores
|
|
|
|
// much the same as a Context but works on a specific module.
|
|
|
|
type walkContext struct {
|
|
|
|
Context *Context
|
|
|
|
Path []string
|
|
|
|
}
|
2014-07-03 20:27:30 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *walkContext) Refresh() error {
|
|
|
|
return nil
|
2014-07-03 20:27:30 +02:00
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *walkContext) applyWalkFn() depgraph.WalkFunc {
|
2014-07-07 08:03:51 +02:00
|
|
|
cb := func(r *Resource) error {
|
2014-07-18 00:32:19 +02:00
|
|
|
var err error
|
|
|
|
|
2014-07-03 20:04:04 +02:00
|
|
|
diff := r.Diff
|
|
|
|
if diff.Empty() {
|
2014-07-07 21:53:01 +02:00
|
|
|
log.Printf("[DEBUG] %s: Diff is empty. Will not apply.", r.Id)
|
2014-07-07 08:03:51 +02:00
|
|
|
return nil
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
|
|
|
|
2014-09-22 07:08:21 +02:00
|
|
|
is := r.State
|
|
|
|
if is == nil {
|
|
|
|
is = new(InstanceState)
|
|
|
|
}
|
|
|
|
is.init()
|
2014-09-16 21:12:15 +02:00
|
|
|
|
2014-07-03 20:04:04 +02:00
|
|
|
if !diff.Destroy {
|
2014-07-08 01:56:23 +02:00
|
|
|
// Since we need the configuration, interpolate the variables
|
|
|
|
if err := r.Config.interpolate(c); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-09-22 07:08:21 +02:00
|
|
|
diff, err = r.Provider.Diff(r.Info, is, r.Config)
|
2014-07-03 20:04:04 +02:00
|
|
|
if err != nil {
|
2014-07-07 08:03:51 +02:00
|
|
|
return err
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
|
|
|
|
2014-07-23 04:32:46 +02:00
|
|
|
// This should never happen because we check if Diff.Empty above.
|
|
|
|
// If this happened, then the diff above returned a bad diff.
|
|
|
|
if diff == nil {
|
|
|
|
return fmt.Errorf(
|
2014-07-23 04:43:09 +02:00
|
|
|
"%s: diff became nil during Apply. This is a bug with "+
|
|
|
|
"the resource provider. Please report a bug.",
|
|
|
|
r.Id)
|
2014-07-23 04:32:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete id from the diff because it is dependent on
|
|
|
|
// our internal plan function.
|
|
|
|
delete(r.Diff.Attributes, "id")
|
|
|
|
delete(diff.Attributes, "id")
|
|
|
|
|
|
|
|
// Verify the diffs are the same
|
|
|
|
if !r.Diff.Same(diff) {
|
|
|
|
log.Printf(
|
|
|
|
"[ERROR] Diffs don't match.\n\nDiff 1: %#v"+
|
|
|
|
"\n\nDiff 2: %#v",
|
|
|
|
r.Diff, diff)
|
|
|
|
return fmt.Errorf(
|
2014-07-23 04:43:09 +02:00
|
|
|
"%s: diffs didn't match during apply. This is a "+
|
|
|
|
"bug with the resource provider, please report a bug.",
|
|
|
|
r.Id)
|
2014-07-23 04:32:46 +02:00
|
|
|
}
|
2014-07-12 05:20:08 +02:00
|
|
|
}
|
|
|
|
|
2014-07-11 21:00:41 +02:00
|
|
|
// Remove any output values from the diff
|
|
|
|
for k, ad := range diff.Attributes {
|
|
|
|
if ad.Type == DiffAttrOutput {
|
|
|
|
delete(diff.Attributes, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
for _, h := range c.Context.hooks {
|
2014-09-22 07:08:21 +02:00
|
|
|
handleHook(h.PreApply(r.Id, is, diff))
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// With the completed diff, apply!
|
|
|
|
log.Printf("[DEBUG] %s: Executing Apply", r.Id)
|
2014-09-22 07:08:21 +02:00
|
|
|
is, applyerr := r.Provider.Apply(r.Info, is, diff)
|
2014-07-18 00:32:19 +02:00
|
|
|
|
|
|
|
var errs []error
|
|
|
|
if applyerr != nil {
|
|
|
|
errs = append(errs, applyerr)
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the result is instantiated
|
2014-09-17 02:10:34 +02:00
|
|
|
if is == nil {
|
|
|
|
is = new(InstanceState)
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
2014-09-17 02:19:28 +02:00
|
|
|
is.init()
|
2014-07-03 20:04:04 +02:00
|
|
|
|
2014-07-09 02:15:41 +02:00
|
|
|
// Force the "id" attribute to be our ID
|
2014-09-17 02:10:34 +02:00
|
|
|
if is.ID != "" {
|
|
|
|
is.Attributes["id"] = is.ID
|
2014-07-09 02:15:41 +02:00
|
|
|
}
|
|
|
|
|
2014-09-17 02:10:34 +02:00
|
|
|
for ak, av := range is.Attributes {
|
2014-07-03 20:04:04 +02:00
|
|
|
// If the value is the unknown variable value, then it is an error.
|
|
|
|
// In this case we record the error and remove it from the state
|
|
|
|
if av == config.UnknownVariableValue {
|
|
|
|
errs = append(errs, fmt.Errorf(
|
|
|
|
"Attribute with unknown value: %s", ak))
|
2014-09-17 02:10:34 +02:00
|
|
|
delete(is.Attributes, ak)
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-22 07:08:21 +02:00
|
|
|
// Set the result state
|
|
|
|
r.State = is
|
|
|
|
c.persistState(r)
|
2014-07-24 16:58:45 +02:00
|
|
|
|
2014-07-08 23:01:27 +02:00
|
|
|
// Invoke any provisioners we have defined. This is only done
|
|
|
|
// if the resource was created, as updates or deletes do not
|
|
|
|
// invoke provisioners.
|
2014-07-18 00:32:19 +02:00
|
|
|
//
|
|
|
|
// Additionally, we need to be careful to not run this if there
|
|
|
|
// was an error during the provider apply.
|
2014-07-22 19:09:11 +02:00
|
|
|
tainted := false
|
2014-09-22 07:08:21 +02:00
|
|
|
if applyerr == nil && is.ID != "" && len(r.Provisioners) > 0 {
|
2014-09-23 22:21:45 +02:00
|
|
|
for _, h := range c.Context.hooks {
|
2014-09-22 07:08:21 +02:00
|
|
|
handleHook(h.PreProvisionResource(r.Id, is))
|
2014-07-27 18:00:34 +02:00
|
|
|
}
|
|
|
|
|
2014-09-17 01:20:11 +02:00
|
|
|
if err := c.applyProvisioners(r, is); err != nil {
|
2014-07-08 23:01:27 +02:00
|
|
|
errs = append(errs, err)
|
2014-07-22 19:09:11 +02:00
|
|
|
tainted = true
|
2014-07-08 23:01:27 +02:00
|
|
|
}
|
2014-07-27 18:00:34 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
for _, h := range c.Context.hooks {
|
2014-09-22 07:08:21 +02:00
|
|
|
handleHook(h.PostProvisionResource(r.Id, is))
|
2014-07-27 18:00:34 +02:00
|
|
|
}
|
2014-07-08 23:01:27 +02:00
|
|
|
}
|
|
|
|
|
2014-09-22 07:08:21 +02:00
|
|
|
// If we're tainted then we need to update some flags
|
|
|
|
if tainted && r.Flags&FlagTainted == 0 {
|
2014-09-21 02:02:31 +02:00
|
|
|
r.Flags &^= FlagPrimary
|
|
|
|
r.Flags &^= FlagHasTainted
|
|
|
|
r.Flags |= FlagTainted
|
2014-09-22 07:08:21 +02:00
|
|
|
r.TaintedIndex = -1
|
|
|
|
c.persistState(r)
|
2014-09-21 02:02:31 +02:00
|
|
|
}
|
2014-07-03 20:04:04 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
for _, h := range c.Context.hooks {
|
2014-09-22 07:08:21 +02:00
|
|
|
handleHook(h.PostApply(r.Id, is, applyerr))
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Determine the new state and update variables
|
|
|
|
err = nil
|
|
|
|
if len(errs) > 0 {
|
|
|
|
err = &multierror.Error{Errors: errs}
|
|
|
|
}
|
|
|
|
|
2014-07-07 08:03:51 +02:00
|
|
|
return err
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
|
|
|
|
2014-07-07 06:53:22 +02:00
|
|
|
return c.genericWalkFn(cb)
|
2014-07-03 20:04:04 +02:00
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *walkContext) planWalkFn(result *Plan) depgraph.WalkFunc {
|
2014-07-03 19:44:30 +02:00
|
|
|
var l sync.Mutex
|
|
|
|
|
|
|
|
// Initialize the result
|
|
|
|
result.init()
|
|
|
|
|
2014-07-07 08:03:51 +02:00
|
|
|
cb := func(r *Resource) error {
|
2014-09-21 02:02:31 +02:00
|
|
|
if r.Flags&FlagTainted != 0 {
|
2014-09-20 06:47:53 +02:00
|
|
|
// We don't diff tainted resources.
|
2014-09-20 06:35:29 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
var diff *InstanceDiff
|
2014-07-03 19:44:30 +02:00
|
|
|
|
2014-09-22 07:08:21 +02:00
|
|
|
is := r.State
|
2014-09-18 02:18:03 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
for _, h := range c.Context.hooks {
|
2014-09-18 02:18:03 +02:00
|
|
|
handleHook(h.PreDiff(r.Id, is))
|
2014-07-03 19:44:30 +02:00
|
|
|
}
|
|
|
|
|
2014-09-21 02:02:31 +02:00
|
|
|
if r.Flags&FlagOrphan != 0 {
|
2014-07-03 19:44:30 +02:00
|
|
|
log.Printf("[DEBUG] %s: Orphan, marking for destroy", r.Id)
|
|
|
|
|
|
|
|
// This is an orphan (no config), so we mark it to be destroyed
|
2014-09-18 01:33:24 +02:00
|
|
|
diff = &InstanceDiff{Destroy: true}
|
2014-07-03 19:44:30 +02:00
|
|
|
} else {
|
2014-07-08 01:56:23 +02:00
|
|
|
// Make sure the configuration is interpolated
|
|
|
|
if err := r.Config.interpolate(c); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-07-03 19:44:30 +02:00
|
|
|
|
|
|
|
// Get a diff from the newest state
|
2014-07-08 01:56:23 +02:00
|
|
|
log.Printf("[DEBUG] %s: Executing diff", r.Id)
|
2014-07-03 19:44:30 +02:00
|
|
|
var err error
|
2014-09-22 07:08:21 +02:00
|
|
|
|
|
|
|
diffIs := is
|
|
|
|
if diffIs == nil || r.Flags&FlagHasTainted != 0 {
|
2014-07-22 19:30:42 +02:00
|
|
|
// If we're tainted, we pretend to create a new thing.
|
2014-09-22 07:08:21 +02:00
|
|
|
diffIs = new(InstanceState)
|
2014-07-22 19:30:42 +02:00
|
|
|
}
|
2014-09-22 07:08:21 +02:00
|
|
|
diffIs.init()
|
|
|
|
|
|
|
|
diff, err = r.Provider.Diff(r.Info, diffIs, r.Config)
|
2014-07-03 19:44:30 +02:00
|
|
|
if err != nil {
|
2014-07-07 08:03:51 +02:00
|
|
|
return err
|
2014-07-03 19:44:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-09 01:58:31 +02:00
|
|
|
if diff == nil {
|
2014-09-18 01:33:24 +02:00
|
|
|
diff = new(InstanceDiff)
|
2014-07-09 01:58:31 +02:00
|
|
|
}
|
|
|
|
|
2014-09-21 02:02:31 +02:00
|
|
|
if r.Flags&FlagHasTainted != 0 {
|
|
|
|
// This primary has a tainted resource, so just mark for
|
|
|
|
// destroy...
|
|
|
|
log.Printf("[DEBUG] %s: Tainted children, marking for destroy", r.Id)
|
2014-09-20 06:47:53 +02:00
|
|
|
diff.DestroyTainted = true
|
2014-07-22 19:30:42 +02:00
|
|
|
}
|
|
|
|
|
2014-09-18 02:18:03 +02:00
|
|
|
if diff.RequiresNew() && is != nil && is.ID != "" {
|
2014-07-09 01:58:31 +02:00
|
|
|
// This will also require a destroy
|
|
|
|
diff.Destroy = true
|
|
|
|
}
|
|
|
|
|
2014-09-18 02:18:03 +02:00
|
|
|
if diff.RequiresNew() || is == nil || is.ID == "" {
|
2014-09-17 01:21:09 +02:00
|
|
|
var oldID string
|
2014-09-18 02:18:03 +02:00
|
|
|
if is != nil {
|
|
|
|
oldID = is.Attributes["id"]
|
2014-09-17 01:21:09 +02:00
|
|
|
}
|
|
|
|
|
2014-07-09 01:58:31 +02:00
|
|
|
// Add diff to compute new ID
|
|
|
|
diff.init()
|
|
|
|
diff.Attributes["id"] = &ResourceAttrDiff{
|
2014-09-17 01:21:09 +02:00
|
|
|
Old: oldID,
|
2014-07-09 01:58:31 +02:00
|
|
|
NewComputed: true,
|
|
|
|
RequiresNew: true,
|
|
|
|
Type: DiffAttrOutput,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-03 19:44:30 +02:00
|
|
|
l.Lock()
|
|
|
|
if !diff.Empty() {
|
2014-09-23 20:43:21 +02:00
|
|
|
result.Diff.RootModule().Resources[r.Id] = diff
|
2014-07-03 19:44:30 +02:00
|
|
|
}
|
|
|
|
l.Unlock()
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
for _, h := range c.Context.hooks {
|
2014-07-03 19:44:30 +02:00
|
|
|
handleHook(h.PostDiff(r.Id, diff))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine the new state and update variables
|
|
|
|
if !diff.Empty() {
|
2014-09-18 02:18:03 +02:00
|
|
|
is = is.MergeDiff(diff)
|
2014-07-03 19:44:30 +02:00
|
|
|
}
|
|
|
|
|
2014-09-22 07:08:21 +02:00
|
|
|
// Set it so that it can be updated
|
|
|
|
r.State = is
|
|
|
|
c.persistState(r)
|
2014-07-07 08:03:51 +02:00
|
|
|
|
|
|
|
return nil
|
2014-07-03 19:44:30 +02:00
|
|
|
}
|
|
|
|
|
2014-07-07 06:53:22 +02:00
|
|
|
return c.genericWalkFn(cb)
|
2014-07-03 19:44:30 +02:00
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *walkContext) refreshWalkFn() depgraph.WalkFunc {
|
2014-09-20 06:28:13 +02:00
|
|
|
cb := func(r *Resource) error {
|
2014-09-22 07:08:21 +02:00
|
|
|
is := r.State
|
2014-09-20 06:28:13 +02:00
|
|
|
|
|
|
|
if is == nil || is.ID == "" {
|
2014-07-08 06:12:21 +02:00
|
|
|
log.Printf("[DEBUG] %s: Not refreshing, ID is empty", r.Id)
|
2014-07-08 01:19:25 +02:00
|
|
|
return nil
|
2014-09-23 22:21:45 +02:00
|
|
|
}
|
2014-07-03 21:30:51 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
for _, h := range c.Context.hooks {
|
|
|
|
handleHook(h.PreRefresh(r.Id, is))
|
|
|
|
}
|
2014-07-03 21:17:56 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
is, err := r.Provider.Refresh(r.Info, is)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if is == nil {
|
|
|
|
is = new(InstanceState)
|
|
|
|
is.init()
|
|
|
|
}
|
2014-07-03 21:17:56 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
// Set the updated state
|
|
|
|
r.State = is
|
|
|
|
c.persistState(r)
|
|
|
|
|
|
|
|
for _, h := range c.Context.hooks {
|
|
|
|
handleHook(h.PostRefresh(r.Id, is))
|
2014-07-03 21:17:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2014-09-23 22:21:45 +02:00
|
|
|
|
|
|
|
return c.genericWalkFn(cb)
|
2014-07-03 21:17:56 +02:00
|
|
|
}
|
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc {
|
2014-07-03 19:29:14 +02:00
|
|
|
// This will keep track of whether we're stopped or not
|
|
|
|
var stop uint32 = 0
|
|
|
|
|
|
|
|
return func(n *depgraph.Noun) error {
|
|
|
|
// If it is the root node, ignore
|
|
|
|
if n.Name == GraphRootNode {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we're stopped, return right away
|
|
|
|
if atomic.LoadUint32(&stop) != 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-08 20:42:03 +02:00
|
|
|
// Limit parallelism
|
2014-09-23 22:21:45 +02:00
|
|
|
c.Context.parCh <- struct{}{}
|
2014-07-08 20:42:03 +02:00
|
|
|
defer func() {
|
2014-09-23 22:21:45 +02:00
|
|
|
<-c.Context.parCh
|
2014-07-08 20:42:03 +02:00
|
|
|
}()
|
|
|
|
|
2014-07-03 19:29:14 +02:00
|
|
|
switch m := n.Meta.(type) {
|
2014-09-23 01:48:18 +02:00
|
|
|
case *GraphNodeModule:
|
|
|
|
// TODO
|
|
|
|
return nil
|
2014-07-03 19:29:14 +02:00
|
|
|
case *GraphNodeResource:
|
2014-07-07 08:03:51 +02:00
|
|
|
// Continue, we care about this the most
|
2014-07-04 06:24:17 +02:00
|
|
|
case *GraphNodeResourceMeta:
|
2014-07-07 08:03:51 +02:00
|
|
|
// Skip it
|
2014-07-04 06:24:17 +02:00
|
|
|
return nil
|
2014-07-03 19:29:14 +02:00
|
|
|
case *GraphNodeResourceProvider:
|
2014-07-07 08:03:51 +02:00
|
|
|
// Interpolate in the variables and configure all the providers
|
2014-07-10 20:13:17 +02:00
|
|
|
var raw *config.RawConfig
|
2014-07-03 19:29:14 +02:00
|
|
|
if m.Config != nil {
|
2014-07-10 20:13:17 +02:00
|
|
|
raw = m.Config.RawConfig
|
2014-07-03 19:29:14 +02:00
|
|
|
}
|
|
|
|
|
2014-07-10 20:13:17 +02:00
|
|
|
rc := NewResourceConfig(raw)
|
|
|
|
rc.interpolate(c)
|
|
|
|
|
2014-07-03 19:29:14 +02:00
|
|
|
for k, p := range m.Providers {
|
|
|
|
log.Printf("[INFO] Configuring provider: %s", k)
|
|
|
|
err := p.Configure(rc)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2014-07-04 06:24:17 +02:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unknown graph node: %#v", n.Meta))
|
2014-07-03 19:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
rn := n.Meta.(*GraphNodeResource)
|
|
|
|
|
|
|
|
// Make sure that at least some resource configuration is set
|
2014-09-21 02:02:31 +02:00
|
|
|
if rn.Config == nil {
|
|
|
|
rn.Resource.Config = new(ResourceConfig)
|
2014-07-03 19:29:14 +02:00
|
|
|
} else {
|
2014-09-21 02:02:31 +02:00
|
|
|
rn.Resource.Config = NewResourceConfig(rn.Config.RawConfig)
|
2014-07-03 19:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Handle recovery of special panic scenarios
|
|
|
|
defer func() {
|
|
|
|
if v := recover(); v != nil {
|
|
|
|
if v == HookActionHalt {
|
|
|
|
atomic.StoreUint32(&stop, 1)
|
|
|
|
} else {
|
|
|
|
panic(v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Call the callack
|
2014-07-09 01:58:31 +02:00
|
|
|
log.Printf(
|
|
|
|
"[INFO] Walking: %s (Graph node: %s)",
|
|
|
|
rn.Resource.Id,
|
|
|
|
n.Name)
|
2014-07-07 08:03:51 +02:00
|
|
|
if err := cb(rn.Resource); err != nil {
|
2014-07-08 06:20:48 +02:00
|
|
|
log.Printf("[ERROR] Error walking '%s': %s", rn.Resource.Id, err)
|
2014-07-03 19:29:14 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2014-09-22 07:08:21 +02:00
|
|
|
|
2014-09-23 22:21:45 +02:00
|
|
|
// applyProvisioners is used to run any provisioners a resource has
|
|
|
|
// defined after the resource creation has already completed.
|
|
|
|
func (c *walkContext) applyProvisioners(r *Resource, is *InstanceState) error {
|
|
|
|
// Store the original connection info, restore later
|
|
|
|
origConnInfo := is.Ephemeral.ConnInfo
|
|
|
|
defer func() {
|
|
|
|
is.Ephemeral.ConnInfo = origConnInfo
|
|
|
|
}()
|
|
|
|
|
|
|
|
for _, prov := range r.Provisioners {
|
|
|
|
// Interpolate since we may have variables that depend on the
|
|
|
|
// local resource.
|
|
|
|
if err := prov.Config.interpolate(c); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interpolate the conn info, since it may contain variables
|
|
|
|
connInfo := NewResourceConfig(prov.ConnInfo)
|
|
|
|
if err := connInfo.interpolate(c); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Merge the connection information
|
|
|
|
overlay := make(map[string]string)
|
|
|
|
if origConnInfo != nil {
|
|
|
|
for k, v := range origConnInfo {
|
|
|
|
overlay[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for k, v := range connInfo.Config {
|
|
|
|
switch vt := v.(type) {
|
|
|
|
case string:
|
|
|
|
overlay[k] = vt
|
|
|
|
case int64:
|
|
|
|
overlay[k] = strconv.FormatInt(vt, 10)
|
|
|
|
case int32:
|
|
|
|
overlay[k] = strconv.FormatInt(int64(vt), 10)
|
|
|
|
case int:
|
|
|
|
overlay[k] = strconv.FormatInt(int64(vt), 10)
|
|
|
|
case float32:
|
|
|
|
overlay[k] = strconv.FormatFloat(float64(vt), 'f', 3, 32)
|
|
|
|
case float64:
|
|
|
|
overlay[k] = strconv.FormatFloat(vt, 'f', 3, 64)
|
|
|
|
case bool:
|
|
|
|
overlay[k] = strconv.FormatBool(vt)
|
|
|
|
default:
|
|
|
|
overlay[k] = fmt.Sprintf("%v", vt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
is.Ephemeral.ConnInfo = overlay
|
|
|
|
|
|
|
|
// Invoke the Provisioner
|
|
|
|
for _, h := range c.Context.hooks {
|
|
|
|
handleHook(h.PreProvision(r.Id, prov.Type))
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := prov.Provisioner.Apply(is, prov.Config); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, h := range c.Context.hooks {
|
|
|
|
handleHook(h.PostProvision(r.Id, prov.Type))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// persistState persists the state in a Resource to the actual final
|
|
|
|
// state location.
|
|
|
|
func (c *walkContext) persistState(r *Resource) {
|
2014-09-22 07:08:21 +02:00
|
|
|
// Acquire a state lock around this whole thing since we're updating that
|
2014-09-23 22:21:45 +02:00
|
|
|
c.Context.sl.Lock()
|
|
|
|
defer c.Context.sl.Unlock()
|
2014-09-22 07:08:21 +02:00
|
|
|
|
|
|
|
// If we have no state, then we don't persist.
|
2014-09-23 22:21:45 +02:00
|
|
|
if c.Context.state == nil {
|
2014-09-22 07:08:21 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the state for this resource. The resource state should always
|
|
|
|
// exist because we call graphInitState before anything that could
|
|
|
|
// potentially call this.
|
2014-09-23 22:21:45 +02:00
|
|
|
module := c.Context.state.ModuleByPath(c.Path)
|
2014-09-22 07:08:21 +02:00
|
|
|
rs := module.Resources[r.Id]
|
|
|
|
if rs == nil {
|
|
|
|
panic(fmt.Sprintf("nil ResourceState for ID: %s", r.Id))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assign the instance state to the proper location
|
|
|
|
if r.Flags&FlagTainted != 0 {
|
|
|
|
if r.TaintedIndex >= 0 {
|
|
|
|
// Tainted with a pre-existing index, just update that spot
|
|
|
|
rs.Tainted[r.TaintedIndex] = r.State
|
|
|
|
} else {
|
|
|
|
// Newly tainted, so append it to the list, update the
|
|
|
|
// index, and remove the primary.
|
|
|
|
rs.Tainted = append(rs.Tainted, r.State)
|
|
|
|
rs.Primary = nil
|
|
|
|
r.TaintedIndex = len(rs.Tainted) - 1
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// The primary instance, so just set it directly
|
|
|
|
rs.Primary = r.State
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do a pruning so that empty resources are not saved
|
|
|
|
rs.prune()
|
|
|
|
}
|
2014-09-23 22:21:45 +02:00
|
|
|
|
|
|
|
// computeVars takes the State and given RawConfig and processes all
|
|
|
|
// the variables. This dynamically discovers the attributes instead of
|
|
|
|
// using a static map[string]string that the genericWalkFn uses.
|
|
|
|
func (c *walkContext) computeVars(raw *config.RawConfig) error {
|
|
|
|
// If there isn't a raw configuration, don't do anything
|
|
|
|
if raw == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start building up the variables. First, defaults
|
|
|
|
vs := make(map[string]string)
|
|
|
|
for k, v := range c.Context.defaultVars {
|
|
|
|
vs[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, the actual computed variables
|
|
|
|
for n, rawV := range raw.Variables {
|
|
|
|
switch v := rawV.(type) {
|
|
|
|
case *config.ResourceVariable:
|
|
|
|
var attr string
|
|
|
|
var err error
|
|
|
|
if v.Multi && v.Index == -1 {
|
|
|
|
attr, err = c.computeResourceMultiVariable(v)
|
|
|
|
} else {
|
|
|
|
attr, err = c.computeResourceVariable(v)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
vs[n] = attr
|
|
|
|
case *config.UserVariable:
|
|
|
|
val, ok := c.Context.variables[v.Name]
|
|
|
|
if ok {
|
|
|
|
vs[n] = val
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look up if we have any variables with this prefix because
|
|
|
|
// those are map overrides. Include those.
|
|
|
|
for k, val := range c.Context.variables {
|
|
|
|
if strings.HasPrefix(k, v.Name+".") {
|
|
|
|
vs["var."+k] = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interpolate the variables
|
|
|
|
return raw.Interpolate(vs)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *walkContext) computeResourceVariable(
|
|
|
|
v *config.ResourceVariable) (string, error) {
|
|
|
|
id := v.ResourceId()
|
|
|
|
if v.Multi {
|
|
|
|
id = fmt.Sprintf("%s.%d", id, v.Index)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Context.sl.RLock()
|
|
|
|
defer c.Context.sl.RUnlock()
|
|
|
|
|
|
|
|
// Get the relevant module
|
|
|
|
// TODO: Not use only root module
|
|
|
|
module := c.Context.state.RootModule()
|
|
|
|
|
|
|
|
r, ok := module.Resources[id]
|
|
|
|
if !ok {
|
|
|
|
return "", fmt.Errorf(
|
|
|
|
"Resource '%s' not found for variable '%s'",
|
|
|
|
id,
|
|
|
|
v.FullKey())
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.Primary == nil {
|
|
|
|
goto MISSING
|
|
|
|
}
|
|
|
|
|
|
|
|
if attr, ok := r.Primary.Attributes[v.Field]; ok {
|
|
|
|
return attr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// We didn't find the exact field, so lets separate the dots
|
|
|
|
// and see if anything along the way is a computed set. i.e. if
|
|
|
|
// we have "foo.0.bar" as the field, check to see if "foo" is
|
|
|
|
// a computed list. If so, then the whole thing is computed.
|
|
|
|
if parts := strings.Split(v.Field, "."); len(parts) > 1 {
|
|
|
|
for i := 1; i < len(parts); i++ {
|
|
|
|
key := fmt.Sprintf("%s.#", strings.Join(parts[:i], "."))
|
|
|
|
if attr, ok := r.Primary.Attributes[key]; ok {
|
|
|
|
return attr, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MISSING:
|
|
|
|
return "", fmt.Errorf(
|
|
|
|
"Resource '%s' does not have attribute '%s' "+
|
|
|
|
"for variable '%s'",
|
|
|
|
id,
|
|
|
|
v.Field,
|
|
|
|
v.FullKey())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *walkContext) computeResourceMultiVariable(
|
|
|
|
v *config.ResourceVariable) (string, error) {
|
|
|
|
c.Context.sl.RLock()
|
|
|
|
defer c.Context.sl.RUnlock()
|
|
|
|
|
|
|
|
// Get the resource from the configuration so we can know how
|
|
|
|
// many of the resource there is.
|
|
|
|
var cr *config.Resource
|
|
|
|
for _, r := range c.Context.config.Resources {
|
|
|
|
if r.Id() == v.ResourceId() {
|
|
|
|
cr = r
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if cr == nil {
|
|
|
|
return "", fmt.Errorf(
|
|
|
|
"Resource '%s' not found for variable '%s'",
|
|
|
|
v.ResourceId(),
|
|
|
|
v.FullKey())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the relevant module
|
|
|
|
// TODO: Not use only root module
|
|
|
|
module := c.Context.state.RootModule()
|
|
|
|
|
|
|
|
var values []string
|
|
|
|
for i := 0; i < cr.Count; i++ {
|
|
|
|
id := fmt.Sprintf("%s.%d", v.ResourceId(), i)
|
|
|
|
|
|
|
|
// If we're dealing with only a single resource, then the
|
|
|
|
// ID doesn't have a trailing index.
|
|
|
|
if cr.Count == 1 {
|
|
|
|
id = v.ResourceId()
|
|
|
|
}
|
|
|
|
|
|
|
|
r, ok := module.Resources[id]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.Primary == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
attr, ok := r.Primary.Attributes[v.Field]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
values = append(values, attr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(values) == 0 {
|
|
|
|
return "", fmt.Errorf(
|
|
|
|
"Resource '%s' does not have attribute '%s' "+
|
|
|
|
"for variable '%s'",
|
|
|
|
v.ResourceId(),
|
|
|
|
v.Field,
|
|
|
|
v.FullKey())
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Join(values, ","), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|