diff --git a/terraform/context.go b/terraform/context.go index 34880eb5b..bc8564ca9 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -387,3 +387,16 @@ func (c *Context2) walk(operation walkOperation) (*ContextGraphWalker, error) { walker := &ContextGraphWalker{Context: c, Operation: operation} return walker, graph.Walk(walker) } + +// walkOperation is an enum which tells the walkContext what to do. +type walkOperation byte + +const ( + walkInvalid walkOperation = iota + walkInput + walkApply + walkPlan + walkPlanDestroy + walkRefresh + walkValidate +) diff --git a/terraform/context_old.go b/terraform/context_old.go deleted file mode 100644 index cb0ec40ed..000000000 --- a/terraform/context_old.go +++ /dev/null @@ -1,1539 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "sort" - "strconv" - "strings" - "sync" - "sync/atomic" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/config/module" - "github.com/hashicorp/terraform/depgraph" - "github.com/hashicorp/terraform/helper/multierror" -) - -// This is a function type used to implement a walker for the resource -// tree internally on the Terraform structure. -type genericWalkFunc func(*walkContext, *Resource) error - -// 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 { - module *module.Tree - diff *Diff - hooks []Hook - state *State - providerConfig map[string]map[string]map[string]interface{} - providers map[string]ResourceProviderFactory - provisioners map[string]ResourceProvisionerFactory - variables map[string]string - uiInput UIInput - - parallelSem Semaphore // Semaphore used to limit parallelism - l sync.Mutex // Lock acquired during any task - sl sync.RWMutex // Lock acquired to R/W internal data - runCh <-chan struct{} - sh *stopHook -} - -// 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 { - 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 - - // Make the parallelism channel - par := opts.Parallelism - if par == 0 { - par = 10 - } - - return &Context{ - diff: opts.Diff, - hooks: hooks, - module: opts.Module, - state: opts.State, - providerConfig: make(map[string]map[string]map[string]interface{}), - providers: opts.Providers, - provisioners: opts.Provisioners, - variables: opts.Variables, - uiInput: opts.UIInput, - - parallelSem: NewSemaphore(par), - sh: sh, - } -} - -// 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) { - v := c.acquireRun() - defer c.releaseRun(v) - - // 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() - if c.state == nil { - c.state = &State{} - } - c.state.init() - - // Walk - log.Printf("[INFO] Apply walk starting") - err := c.walkContext(walkApply, rootModulePath).Walk() - log.Printf("[INFO] Apply walk complete") - - // Prune the state so that we have as clean a state as possible - c.state.prune() - - return c.state, err -} - -// Graph returns the graph for this context. -func (c *Context) Graph() (*depgraph.Graph, error) { - return GraphOld(&GraphOpts{ - Diff: c.diff, - Module: c.module, - Providers: c.providers, - Provisioners: c.provisioners, - State: c.state, - }) -} - -// Input asks for input to fill variables and provider configurations. -// This modifies the configuration in-place, so asking for Input twice -// may result in different UI output showing different current values. -func (c *Context) Input(mode InputMode) error { - v := c.acquireRun() - defer c.releaseRun(v) - - if mode&InputModeVar != 0 { - // Walk the variables first for the root module. We walk them in - // alphabetical order for UX reasons. - rootConf := c.module.Config() - names := make([]string, len(rootConf.Variables)) - m := make(map[string]*config.Variable) - for i, v := range rootConf.Variables { - names[i] = v.Name - m[v.Name] = v - } - sort.Strings(names) - for _, n := range names { - v := m[n] - switch v.Type() { - case config.VariableTypeMap: - continue - case config.VariableTypeString: - // Good! - default: - panic(fmt.Sprintf("Unknown variable type: %#v", v.Type())) - } - - var defaultString string - if v.Default != nil { - defaultString = v.Default.(string) - } - - // Ask the user for a value for this variable - var value string - for { - var err error - value, err = c.uiInput.Input(&InputOpts{ - Id: fmt.Sprintf("var.%s", n), - Query: fmt.Sprintf("var.%s", n), - Default: defaultString, - Description: v.Description, - }) - if err != nil { - return fmt.Errorf( - "Error asking for %s: %s", n, err) - } - - if value == "" && v.Required() { - // Redo if it is required. - continue - } - - if value == "" { - // No value, just exit the loop. With no value, we just - // use whatever is currently set in variables. - break - } - - break - } - - if value != "" { - c.variables[n] = value - } - } - } - - if mode&InputModeProvider != 0 { - // Create the walk context and walk the inputs, which will gather the - // inputs for any resource providers. - wc := c.walkContext(walkInput, rootModulePath) - wc.Meta = new(walkInputMeta) - return wc.Walk() - } - - return nil -} - -// 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. -// -// Plan also updates the diff of this context to be the diff generated -// by the plan, so Apply can be called after. -func (c *Context) Plan(opts *PlanOpts) (*Plan, error) { - v := c.acquireRun() - defer c.releaseRun(v) - - p := &Plan{ - Module: c.module, - Vars: c.variables, - State: c.state, - } - - wc := c.walkContext(walkInvalid, rootModulePath) - wc.Meta = p - - if opts != nil && opts.Destroy { - wc.Operation = walkPlanDestroy - } 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 - if old == nil { - c.state = &State{} - c.state.init() - } else { - c.state = old.deepcopy() - } - defer func() { - c.state = old - }() - - wc.Operation = walkPlan - } - - // Walk and run the plan - err := wc.Walk() - - // Update the diff so that our context is up-to-date - c.diff = p.Diff - - return p, err -} - -// 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) { - v := c.acquireRun() - defer c.releaseRun(v) - - // Update our state - c.state = c.state.deepcopy() - - // Walk the graph - err := c.walkContext(walkRefresh, rootModulePath).Walk() - - // Prune the state - c.state.prune() - return c.state, err -} - -// 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 -} - -// 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.module.Validate(); err != nil { - rerr = multierror.ErrorAppend(rerr, err) - } - - // This only needs to be done for the root module, since inter-module - // variables are validated in the module tree. - if config := c.module.Config(); config != nil { - // Validate the user variables - if errs := smcUserVariables(config, c.variables); len(errs) > 0 { - rerr = multierror.ErrorAppend(rerr, errs...) - } - } - - // Validate the entire graph - walkMeta := new(walkValidateMeta) - wc := c.walkContext(walkValidate, rootModulePath) - wc.Meta = walkMeta - if err := wc.Walk(); err != nil { - rerr = multierror.ErrorAppend(rerr, err) - } - - // Flatten the warns/errs so that we get all the module errors as well, - // then aggregate. - warns, errs := walkMeta.Flatten() - if len(errs) > 0 { - rerr = multierror.ErrorAppend(rerr, errs...) - } - - errs = nil - if rerr != nil && len(rerr.Errors) > 0 { - errs = rerr.Errors - } - - return warns, errs -} - -func (c *Context) acquireRun() chan<- struct{} { - c.l.Lock() - defer c.l.Unlock() - - // Wait for no channel to exist - for c.runCh != nil { - c.l.Unlock() - ch := c.runCh - <-ch - c.l.Lock() - } - - ch := make(chan struct{}) - c.runCh = ch - return ch -} - -func (c *Context) releaseRun(ch chan<- struct{}) { - c.l.Lock() - defer c.l.Unlock() - - close(ch) - c.runCh = nil - c.sh.Reset() -} - -func (c *Context) walkContext(op walkOperation, path []string) *walkContext { - return &walkContext{ - Context: c, - Operation: op, - Path: path, - Variables: c.variables, - } -} - -// 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 - Meta interface{} - Operation walkOperation - Path []string - Variables map[string]string - - defaultVariables map[string]string - - // This is only set manually by subsequent context creations - // in genericWalkFunc. - graph *depgraph.Graph -} - -// walkOperation is an enum which tells the walkContext what to do. -type walkOperation byte - -const ( - walkInvalid walkOperation = iota - walkInput - walkApply - walkPlan - walkPlanDestroy - walkRefresh - walkValidate -) - -func (c *walkContext) Walk() error { - g := c.graph - if g == nil { - gopts := &GraphOpts{ - Module: c.Context.module, - Providers: c.Context.providers, - Provisioners: c.Context.provisioners, - State: c.Context.state, - } - if c.Operation == walkApply { - gopts.Diff = c.Context.diff - } - - var err error - g, err = GraphOld(gopts) - if err != nil { - return err - } - } - - var walkFn depgraph.WalkFunc - switch c.Operation { - case walkInput: - walkFn = c.inputWalkFn() - case walkApply: - walkFn = c.applyWalkFn() - case walkPlan: - walkFn = c.planWalkFn() - case walkPlanDestroy: - walkFn = c.planDestroyWalkFn() - case walkRefresh: - walkFn = c.refreshWalkFn() - case walkValidate: - walkFn = c.validateWalkFn() - default: - panic(fmt.Sprintf("unknown operation: %#v", c.Operation)) - } - - if err := g.Walk(walkFn); err != nil { - return err - } - - switch c.Operation { - case walkInput: - fallthrough - case walkValidate: - // Don't calculate outputs - return nil - } - - // We did an apply, so we need to calculate the outputs. If we have no - // outputs, then we're done. - m := c.Context.module - for _, n := range c.Path[1:] { - cs := m.Children() - m = cs[n] - } - if m == nil { - return nil - } - conf := m.Config() - if len(conf.Outputs) == 0 { - return nil - } - - // Likewise, if we have no resources in our state, we're done. This - // guards against the case that we destroyed. - mod := c.Context.state.ModuleByPath(c.Path) - if mod == nil { - return nil - } - if c.Operation == walkApply { - // On Apply, we prune so that we don't do outputs if we destroyed - mod.prune() - } - if len(mod.Resources) == 0 && len(conf.Resources) != 0 { - mod.Outputs = nil - return nil - } - - outputs := make(map[string]string) - for _, o := range conf.Outputs { - if err := c.computeVars(o.RawConfig, nil); err != nil { - // If we're refreshing, then we ignore output errors. This is - // properly not fully the correct behavior, but fixes a range - // of issues right now. As we expand test cases to find the - // correct behavior, this will likely be removed. - if c.Operation == walkRefresh { - continue - } - - return err - } - vraw := o.RawConfig.Config()["value"] - if vraw == nil { - // This likely means that the result of the output is - // a computed variable. - if o.RawConfig.Raw["value"] != nil { - vraw = config.UnknownVariableValue - } - } - if vraw != nil { - if list, ok := vraw.([]interface{}); ok { - vraw = list[0] - } - if s, ok := vraw.(string); ok { - if s != config.UnknownVariableValue { - outputs[o.Name] = s - } - } else { - return fmt.Errorf("Type of output '%s' is not a string: %#v", o.Name, vraw) - } - } - } - - // Assign the outputs to the root module - mod.Outputs = outputs - - return nil -} - -func (c *walkContext) inputWalkFn() depgraph.WalkFunc { - meta := c.Meta.(*walkInputMeta) - meta.Lock() - if meta.Done == nil { - meta.Done = make(map[string]struct{}) - } - meta.Unlock() - - return func(n *depgraph.Noun) error { - // If it is the root node, ignore - if n.Name == GraphRootNode { - return nil - } - - switch rn := n.Meta.(type) { - case *GraphNodeModule: - // Build another walkContext for this module and walk it. - wc := c.Context.walkContext(c.Operation, rn.Path) - - // Set the graph to specifically walk this subgraph - wc.graph = rn.Graph - - // Preserve the meta - wc.Meta = c.Meta - - return wc.Walk() - case *GraphNodeResource: - // Resources don't matter for input. Continue. - return nil - case *GraphNodeResourceProvider: - // Acquire the lock the whole time so we only ask for input - // one at a time. - meta.Lock() - defer meta.Unlock() - - // If we already did this provider, then we're done. - if _, ok := meta.Done[rn.ID]; ok { - return nil - } - - // Get the raw configuration because this is what we - // pass into the API. - var raw *config.RawConfig - sharedProvider := rn.Provider - if sharedProvider.Config != nil { - raw = sharedProvider.Config.RawConfig - } - rc := NewResourceConfig(raw) - rc.Config = make(map[string]interface{}) - - // Wrap the input into a namespace - input := &PrefixUIInput{ - IdPrefix: fmt.Sprintf("provider.%s", rn.ID), - QueryPrefix: fmt.Sprintf("provider.%s.", rn.ID), - UIInput: c.Context.uiInput, - } - - // Go through each provider and capture the input necessary - // to satisfy it. - configs := make(map[string]map[string]interface{}) - for k, p := range sharedProvider.Providers { - newc, err := p.Input(input, rc) - if err != nil { - return fmt.Errorf( - "Error configuring %s: %s", k, err) - } - if newc != nil && len(newc.Config) > 0 { - configs[k] = newc.Config - } - } - - // Mark this provider as done - meta.Done[rn.ID] = struct{}{} - - // Set the configuration - c.Context.providerConfig[rn.ID] = configs - } - - return nil - } -} - -func (c *walkContext) applyWalkFn() depgraph.WalkFunc { - cb := func(c *walkContext, r *Resource) error { - var err error - - diff := r.Diff - if diff.Empty() { - log.Printf("[DEBUG] %s: Diff is empty. Will not apply.", r.Id) - return nil - } - - is := r.State - if is == nil { - is = new(InstanceState) - } - is.init() - - if !diff.Destroy { - // Since we need the configuration, interpolate the variables - if err := r.Config.interpolate(c, r); err != nil { - return err - } - - diff, err = r.Provider.Diff(r.Info, is, r.Config) - if err != nil { - return err - } - - // This can happen if we aren't actually applying anything - // except an ID (the "null" provider). It is not really an issue - // since the Same check later down will catch any real problems. - if diff == nil { - diff = new(InstanceDiff) - diff.init() - } - - // 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( - "%s: diffs didn't match during apply. This is a "+ - "bug with the resource provider, please report a bug.", - r.Id) - } - } - - // Remove any output values from the diff - for k, ad := range diff.Attributes { - if ad.Type == DiffAttrOutput { - delete(diff.Attributes, k) - } - } - - for _, h := range c.Context.hooks { - handleHook(h.PreApply(r.Info, is, diff)) - } - - // We create a new instance if there was no ID - // previously or the diff requires re-creating the - // underlying instance - createNew := (is.ID == "" && !diff.Destroy) || diff.RequiresNew() - - // With the completed diff, apply! - log.Printf("[DEBUG] %s: Executing Apply", r.Id) - is, applyerr := r.Provider.Apply(r.Info, is, diff) - - var errs []error - if applyerr != nil { - errs = append(errs, applyerr) - } - - // Make sure the result is instantiated - if is == nil { - is = new(InstanceState) - } - is.init() - - // Force the "id" attribute to be our ID - if is.ID != "" { - is.Attributes["id"] = is.ID - } - - for ak, av := range is.Attributes { - // 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)) - delete(is.Attributes, ak) - } - } - - // Set the result state - r.State = is - c.persistState(r) - - // Invoke any provisioners we have defined. This is only done - // if the resource was created, as updates or deletes do not - // invoke provisioners. - // - // Additionally, we need to be careful to not run this if there - // was an error during the provider apply. - tainted := false - if createNew && len(r.Provisioners) > 0 { - if applyerr == nil { - // If the apply succeeded, we have to run the provisioners - for _, h := range c.Context.hooks { - handleHook(h.PreProvisionResource(r.Info, is)) - } - - if err := c.applyProvisioners(r, is); err != nil { - errs = append(errs, err) - tainted = true - } - - for _, h := range c.Context.hooks { - handleHook(h.PostProvisionResource(r.Info, is)) - } - } else { - // If we failed to create properly and we have provisioners, - // then we have to mark ourselves as tainted to try again. - tainted = true - } - } - - // If we're tainted then we need to update some flags - if tainted && r.Flags&FlagTainted == 0 { - r.Flags &^= FlagPrimary - r.Flags &^= FlagHasTainted - r.Flags |= FlagTainted - r.TaintedIndex = -1 - c.persistState(r) - } - - for _, h := range c.Context.hooks { - handleHook(h.PostApply(r.Info, is, applyerr)) - } - - // Determine the new state and update variables - err = nil - if len(errs) > 0 { - err = &multierror.Error{Errors: errs} - } - - return err - } - - return c.genericWalkFn(cb) -} - -func (c *walkContext) planWalkFn() depgraph.WalkFunc { - var l sync.Mutex - - // Initialize the result - result := c.Meta.(*Plan) - result.init() - - cb := func(c *walkContext, r *Resource) error { - if r.Flags&FlagTainted != 0 { - // We don't diff tainted resources. - return nil - } - - var diff *InstanceDiff - - is := r.State - - for _, h := range c.Context.hooks { - handleHook(h.PreDiff(r.Info, is)) - } - - if r.Flags&FlagOrphan != 0 { - log.Printf("[DEBUG] %s: Orphan, marking for destroy", r.Id) - - // This is an orphan (no config), so we mark it to be destroyed - diff = &InstanceDiff{Destroy: true} - } else { - // Make sure the configuration is interpolated - if err := r.Config.interpolate(c, r); err != nil { - return err - } - - // Get a diff from the newest state - log.Printf("[DEBUG] %s: Executing diff", r.Id) - var err error - - diffIs := is - if diffIs == nil || r.Flags&FlagHasTainted != 0 { - // If we're tainted, we pretend to create a new thing. - diffIs = new(InstanceState) - } - diffIs.init() - - diff, err = r.Provider.Diff(r.Info, diffIs, r.Config) - if err != nil { - return err - } - } - - if diff == nil { - diff = new(InstanceDiff) - } - - 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) - diff.DestroyTainted = true - } - - if diff.RequiresNew() && is != nil && is.ID != "" { - // This will also require a destroy - diff.Destroy = true - } - - if diff.RequiresNew() || is == nil || is.ID == "" { - var oldID string - if is != nil { - oldID = is.Attributes["id"] - } - - // Add diff to compute new ID - diff.init() - diff.Attributes["id"] = &ResourceAttrDiff{ - Old: oldID, - NewComputed: true, - RequiresNew: true, - Type: DiffAttrOutput, - } - } - - if !diff.Empty() { - log.Printf("[DEBUG] %s: Diff: %#v", r.Id, diff) - - l.Lock() - md := result.Diff.ModuleByPath(c.Path) - if md == nil { - md = result.Diff.AddModule(c.Path) - } - md.Resources[r.Id] = diff - l.Unlock() - } - - for _, h := range c.Context.hooks { - handleHook(h.PostDiff(r.Info, diff)) - } - - // Determine the new state and update variables - if !diff.Empty() { - is = is.MergeDiff(diff) - } - - // Set it so that it can be updated - r.State = is - c.persistState(r) - - return nil - } - - return c.genericWalkFn(cb) -} - -func (c *walkContext) planDestroyWalkFn() depgraph.WalkFunc { - var l sync.Mutex - - // Initialize the result - result := c.Meta.(*Plan) - result.init() - - var walkFn depgraph.WalkFunc - walkFn = func(n *depgraph.Noun) error { - switch m := n.Meta.(type) { - case *GraphNodeModule: - // Set the destroy bool on the module - md := result.Diff.ModuleByPath(m.Path) - if md == nil { - md = result.Diff.AddModule(m.Path) - } - md.Destroy = true - - // Build another walkContext for this module and walk it. - wc := c.Context.walkContext(c.Operation, m.Path) - - // compute incoming vars - if m.Config != nil { - wc.Variables = make(map[string]string) - - rc := NewResourceConfig(m.Config.RawConfig) - if err := rc.interpolate(c, nil); err != nil { - return err - } - for k, v := range rc.Config { - wc.Variables[k] = v.(string) - } - for k, _ := range rc.Raw { - if _, ok := wc.Variables[k]; !ok { - wc.Variables[k] = config.UnknownVariableValue - } - } - } - - // Set the graph to specifically walk this subgraph - wc.graph = m.Graph - - // Preserve the meta - wc.Meta = c.Meta - - return wc.Walk() - case *GraphNodeResource: - // If we're expanding, then expand the nodes, and then rewalk the graph - if m.ExpandMode > ResourceExpandNone { - return c.genericWalkResource(m, walkFn) - } - - r := m.Resource - - if r.State != nil && r.State.ID != "" { - log.Printf("[DEBUG] %s: Making for destroy", r.Id) - - l.Lock() - defer l.Unlock() - md := result.Diff.ModuleByPath(c.Path) - if md == nil { - md = result.Diff.AddModule(c.Path) - } - md.Resources[r.Id] = &InstanceDiff{Destroy: true} - } else { - log.Printf("[DEBUG] %s: Not marking for destroy, no ID", r.Id) - } - } - - return nil - } - - return walkFn -} - -func (c *walkContext) refreshWalkFn() depgraph.WalkFunc { - cb := func(c *walkContext, r *Resource) error { - is := r.State - - if is == nil || is.ID == "" { - log.Printf("[DEBUG] %s: Not refreshing, ID is empty", r.Id) - return nil - } - - for _, h := range c.Context.hooks { - handleHook(h.PreRefresh(r.Info, is)) - } - - is, err := r.Provider.Refresh(r.Info, is) - if err != nil { - return err - } - if is == nil { - is = new(InstanceState) - is.init() - } - - // Set the updated state - r.State = is - c.persistState(r) - - for _, h := range c.Context.hooks { - handleHook(h.PostRefresh(r.Info, is)) - } - - return nil - } - - return c.genericWalkFn(cb) -} - -func (c *walkContext) validateWalkFn() depgraph.WalkFunc { - var l sync.Mutex - - meta := c.Meta.(*walkValidateMeta) - if meta.Children == nil { - meta.Children = make(map[string]*walkValidateMeta) - } - - var walkFn depgraph.WalkFunc - walkFn = func(n *depgraph.Noun) error { - // If it is the root node, ignore - if n.Name == GraphRootNode { - return nil - } - - switch rn := n.Meta.(type) { - case *GraphNodeModule: - // Build another walkContext for this module and walk it. - wc := c.Context.walkContext(walkValidate, rn.Path) - - // Set the graph to specifically walk this subgraph - wc.graph = rn.Graph - - // Build the meta parameter. Do this by sharing the Children - // reference but copying the rest into our own Children list. - newMeta := new(walkValidateMeta) - newMeta.Children = meta.Children - wc.Meta = newMeta - - if err := wc.Walk(); err != nil { - return err - } - - newMeta.Children = nil - meta.Children[strings.Join(rn.Path, ".")] = newMeta - return nil - case *GraphNodeResource: - if rn.Resource == nil { - panic("resource should never be nil") - } - - // If we're expanding, then expand the nodes, and then rewalk the graph - if rn.ExpandMode > ResourceExpandNone { - // Interpolate the count and verify it is non-negative - rc := NewResourceConfig(rn.Config.RawCount) - if err := rc.interpolate(c, rn.Resource); err != nil { - return err - } - if !rc.IsComputed(rn.Config.RawCount.Key) { - count, err := rn.Config.Count() - if err == nil { - if count < 0 { - err = fmt.Errorf( - "%s error: count must be positive", rn.Resource.Id) - } - } - if err != nil { - l.Lock() - defer l.Unlock() - meta.Errs = append(meta.Errs, err) - return nil - } - } - - return c.genericWalkResource(rn, walkFn) - } - - // If it doesn't have a provider, that is a different problem - if rn.Resource.Provider == nil { - return nil - } - - // Don't validate orphans or tainted since they never have a config - if rn.Resource.Flags&FlagOrphan != 0 { - return nil - } - if rn.Resource.Flags&FlagTainted != 0 { - return nil - } - - // If the resouce name doesn't match the name regular - // expression, show a warning. - if !config.NameRegexp.Match([]byte(rn.Config.Name)) { - l.Lock() - meta.Warns = append(meta.Warns, fmt.Sprintf( - "%s: module name can only contain letters, numbers, "+ - "dashes, and underscores.\n"+ - "This will be an error in Terraform 0.4", - rn.Resource.Id)) - l.Unlock() - } - - // Compute the variables in this resource - rn.Resource.Config.interpolate(c, rn.Resource) - - 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) - } - - l.Lock() - meta.Warns = append(meta.Warns, ws...) - meta.Errs = append(meta.Errs, es...) - l.Unlock() - - 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) - } - - l.Lock() - meta.Warns = append(meta.Warns, ws...) - meta.Errs = append(meta.Errs, es...) - l.Unlock() - } - - case *GraphNodeResourceProvider: - sharedProvider := rn.Provider - - // Check if we have an override - cs, ok := c.Context.providerConfig[rn.ID] - if !ok { - cs = make(map[string]map[string]interface{}) - } - - for k, p := range sharedProvider.Providers { - // Merge the configurations to get what we use to configure with - rc := sharedProvider.MergeConfig(false, cs[k]) - if err := rc.interpolate(c, nil); err != nil { - return err - } - - 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) - } - - l.Lock() - meta.Warns = append(meta.Warns, ws...) - meta.Errs = append(meta.Errs, es...) - l.Unlock() - } - } - - return nil - } - - return walkFn -} - -func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc { - // This will keep track of whether we're stopped or not - var stop uint32 = 0 - - var walkFn depgraph.WalkFunc - walkFn = 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 - } - - switch m := n.Meta.(type) { - case *GraphNodeModule: - // Build another walkContext for this module and walk it. - wc := c.Context.walkContext(c.Operation, m.Path) - - // Set the graph to specifically walk this subgraph - wc.graph = m.Graph - - // Preserve the meta - wc.Meta = c.Meta - - // Set the variables - if m.Config != nil { - wc.Variables = make(map[string]string) - - rc := NewResourceConfig(m.Config.RawConfig) - if err := rc.interpolate(c, nil); err != nil { - return err - } - println(fmt.Sprintf("%#v", rc)) - for k, v := range rc.Config { - wc.Variables[k] = v.(string) - } - for k, _ := range rc.Raw { - if _, ok := wc.Variables[k]; !ok { - wc.Variables[k] = config.UnknownVariableValue - } - } - } - - return wc.Walk() - case *GraphNodeResource: - // Continue, we care about this the most - case *GraphNodeResourceProvider: - sharedProvider := m.Provider - - // Check if we have an override - cs, ok := c.Context.providerConfig[m.ID] - if !ok { - cs = make(map[string]map[string]interface{}) - } - - for k, p := range sharedProvider.Providers { - // Interpolate our own configuration before merging - if sharedProvider.Config != nil { - rc := NewResourceConfig(sharedProvider.Config.RawConfig) - if err := rc.interpolate(c, nil); err != nil { - return err - } - } - - // Merge the configurations to get what we use to configure - // with. We don't need to interpolate this because the - // lines above verify that all parents are interpolated - // properly. - rc := sharedProvider.MergeConfig(false, cs[k]) - - log.Printf("[INFO] Configuring provider: %s", k) - err := p.Configure(rc) - if err != nil { - return err - } - } - - return nil - default: - panic(fmt.Sprintf("unknown graph node: %#v", n.Meta)) - } - - rn := n.Meta.(*GraphNodeResource) - - // If we're expanding, then expand the nodes, and then rewalk the graph - if rn.ExpandMode > ResourceExpandNone { - return c.genericWalkResource(rn, walkFn) - } - - // Make sure that at least some resource configuration is set - if rn.Config == nil { - rn.Resource.Config = new(ResourceConfig) - } else { - rn.Resource.Config = NewResourceConfig(rn.Config.RawConfig) - } - - // Handle recovery of special panic scenarios - defer func() { - if v := recover(); v != nil { - if v == HookActionHalt { - atomic.StoreUint32(&stop, 1) - } else { - panic(v) - } - } - }() - - // Limit parallelism - c.Context.parallelSem.Acquire() - defer c.Context.parallelSem.Release() - - // Call the callack - log.Printf( - "[INFO] Module %s walking: %s (Graph node: %s)", - strings.Join(c.Path, "."), - rn.Resource.Id, - n.Name) - if err := cb(c, rn.Resource); err != nil { - log.Printf("[ERROR] Error walking '%s': %s", rn.Resource.Id, err) - return err - } - - return nil - } - - return walkFn -} - -func (c *walkContext) genericWalkResource( - rn *GraphNodeResource, fn depgraph.WalkFunc) error { - // Interpolate the count - rc := NewResourceConfig(rn.Config.RawCount) - if err := rc.interpolate(c, rn.Resource); err != nil { - return err - } - - // If we're validating, then we set the count to 1 if it is computed - if c.Operation == walkValidate { - if key := rn.Config.RawCount.Key; rc.IsComputed(key) { - // Preserve the old value so that we reset it properly - old := rn.Config.RawCount.Raw[key] - defer func() { - rn.Config.RawCount.Raw[key] = old - }() - - // Set th count to 1 for validation purposes - rn.Config.RawCount.Raw[key] = "1" - } - } - - // Expand the node to the actual resources - g, err := rn.Expand() - if err != nil { - return err - } - - // Walk the graph with our function - if err := g.Walk(fn); err != nil { - return err - } - - return nil -} - -// 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, r); err != nil { - return err - } - - // Interpolate the conn info, since it may contain variables - connInfo := NewResourceConfig(prov.ConnInfo) - if err := connInfo.interpolate(c, r); 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.Info, prov.Type)) - } - - output := ProvisionerUIOutput{ - Info: r.Info, - Type: prov.Type, - Hooks: c.Context.hooks, - } - err := prov.Provisioner.Apply(&output, is, prov.Config) - if err != nil { - return err - } - - for _, h := range c.Context.hooks { - handleHook(h.PostProvision(r.Info, prov.Type)) - } - } - - return nil -} - -// persistState persists the state in a Resource to the actual final -// state location. -func (c *walkContext) persistState(r *Resource) { - // Acquire a state lock around this whole thing since we're updating that - c.Context.sl.Lock() - defer c.Context.sl.Unlock() - - // If we have no state, then we don't persist. - if c.Context.state == nil { - return - } - - // Get the state for this resource. The resource state should always - // exist because we call graphInitState before anything that could - // potentially call this. - module := c.Context.state.ModuleByPath(c.Path) - if module == nil { - module = c.Context.state.AddModule(c.Path) - } - rs := module.Resources[r.Id] - if rs == nil { - rs = &ResourceState{Type: r.Info.Type} - rs.init() - module.Resources[r.Id] = rs - } - rs.Dependencies = r.Dependencies - - // Assign the instance state to the proper location - if r.Flags&FlagDeposed != 0 { - // We were previously the primary and have been deposed, so - // now we are the final tainted resource - r.TaintedIndex = len(rs.Tainted) - 1 - rs.Tainted[r.TaintedIndex] = r.State - - } else 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 if r.Flags&FlagReplacePrimary != 0 { - // We just replaced the primary, so restore the primary - rs.Primary = rs.Tainted[len(rs.Tainted)-1] - - // Set ourselves as tainted - rs.Tainted[len(rs.Tainted)-1] = 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) - r.TaintedIndex = len(rs.Tainted) - 1 - rs.Primary = nil - } - - } else if r.Flags&FlagReplacePrimary != 0 { - // If the ID is blank (there was an error), then we leave - // the primary that exists, and do not store this as a tainted - // instance - if r.State.ID == "" { - return - } - - // Push the old primary into the tainted state - rs.Tainted = append(rs.Tainted, rs.Primary) - - // Set this as the new primary - rs.Primary = r.State - - } 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() -} - -// 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, r *Resource) error { - // If there isn't a raw configuration, don't do anything - if raw == nil { - return nil - } - - // Build the interpolater - i := &Interpolater{ - Operation: c.Operation, - Module: c.Context.module, - State: c.Context.state, - StateLock: &c.Context.sl, - Variables: c.Variables, - } - scope := &InterpolationScope{ - Path: c.Path, - Resource: r, - } - vs, err := i.Values(scope, raw.Variables) - if err != nil { - return err - } - - // Interpolate the variables - return raw.Interpolate(vs) -} - -type walkInputMeta struct { - sync.Mutex - - Done map[string]struct{} -} - -type walkValidateMeta struct { - Errs []error - Warns []string - Children map[string]*walkValidateMeta -} - -func (m *walkValidateMeta) Flatten() ([]string, []error) { - // Prune out the empty children - for k, m2 := range m.Children { - if len(m2.Errs) == 0 && len(m2.Warns) == 0 { - delete(m.Children, k) - } - } - - // If we have no children, then just return what we have - if len(m.Children) == 0 { - return m.Warns, m.Errs - } - - // Otherwise, copy the errors and warnings - errs := make([]error, len(m.Errs)) - warns := make([]string, len(m.Warns)) - for i, err := range m.Errs { - errs[i] = err - } - for i, warn := range m.Warns { - warns[i] = warn - } - - // Now go through each child and copy it in... - for k, c := range m.Children { - for _, err := range c.Errs { - errs = append(errs, fmt.Errorf( - "Module %s: %s", k, err)) - } - for _, warn := range c.Warns { - warns = append(warns, fmt.Sprintf( - "Module %s: %s", k, warn)) - } - } - - return warns, errs -} diff --git a/terraform/context_old_test.go b/terraform/context_old_test.go deleted file mode 100644 index 5ea0655a3..000000000 --- a/terraform/context_old_test.go +++ /dev/null @@ -1,4839 +0,0 @@ -package terraform - -import ( - "bytes" - "fmt" - "os" - "reflect" - "runtime" - "sort" - "strings" - "sync" - "testing" -) - -func TestContextGraph(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-good") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - g, err := c.Graph() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testContextGraph) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} - -func TestContextValidate(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-good") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_goodModule(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-good-module") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_badModuleOutput(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-bad-module-output") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_badVar(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-bad-var") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_countNegative(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-count-negative") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_countVariable(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "apply-count-variable") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - for _, err := range e { - t.Errorf("bad: %s", err) - } - t.FailNow() - } -} - -func TestContextValidate_countVariableNoDefault(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-count-variable") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 1 { - for _, err := range e { - t.Errorf("bad: %s", err) - } - t.FailNow() - } -} - -func TestContextValidate_moduleBadResource(t *testing.T) { - m := testModule(t, "validate-module-bad-rc") - p := testProvider("aws") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - p.ValidateResourceReturnErrors = []error{fmt.Errorf("bad")} - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_moduleProviderInherit(t *testing.T) { - m := testModule(t, "validate-module-pc-inherit") - p := testProvider("aws") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - p.ValidateFn = func(c *ResourceConfig) ([]string, []error) { - return nil, c.CheckSet([]string{"set"}) - } - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_orphans(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-good") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - - p.ValidateResourceFn = func( - t string, c *ResourceConfig) ([]string, []error) { - return nil, c.CheckSet([]string{"foo"}) - } - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_tainted(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-good") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Tainted: []*InstanceState{ - &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - }, - } - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - - p.ValidateResourceFn = func( - t string, c *ResourceConfig) ([]string, []error) { - return nil, c.CheckSet([]string{"foo"}) - } - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_providerConfig_bad(t *testing.T) { - m := testModule(t, "validate-bad-pc") - p := testProvider("aws") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - p.ValidateReturnErrors = []error{fmt.Errorf("bad")} - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_providerConfig_badEmpty(t *testing.T) { - m := testModule(t, "validate-bad-pc-empty") - p := testProvider("aws") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - p.ValidateReturnErrors = []error{fmt.Errorf("bad")} - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_providerConfig_good(t *testing.T) { - m := testModule(t, "validate-bad-pc") - p := testProvider("aws") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_resourceConfig_bad(t *testing.T) { - m := testModule(t, "validate-bad-rc") - p := testProvider("aws") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - p.ValidateResourceReturnErrors = []error{fmt.Errorf("bad")} - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_resourceConfig_good(t *testing.T) { - m := testModule(t, "validate-bad-rc") - p := testProvider("aws") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_resourceNameSymbol(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-resource-name-symbol") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) == 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_requiredVar(t *testing.T) { - m := testModule(t, "validate-required-var") - c := testContext(t, &ContextOpts{ - Module: m, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_provisionerConfig_bad(t *testing.T) { - m := testModule(t, "validate-bad-prov-conf") - p := testProvider("aws") - pr := testProvisioner() - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - }) - - pr.ValidateReturnErrors = []error{fmt.Errorf("bad")} - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_provisionerConfig_good(t *testing.T) { - m := testModule(t, "validate-bad-prov-conf") - p := testProvider("aws") - pr := testProvisioner() - pr.ValidateFn = func(c *ResourceConfig) ([]string, []error) { - if c == nil { - t.Fatalf("missing resource config for provisioner") - } - return nil, nil - } - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_selfRef(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-self-ref") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_selfRefMulti(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-self-ref-multi") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_selfRefMultiAll(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "validate-self-ref-multi-all") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - w, e := c.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) == 0 { - t.Fatalf("bad: %#v", e) - } -} - -func TestContextValidate_varRef(t *testing.T) { - m := testModule(t, "validate-variable-ref") - p := testProvider("aws") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - computed := false - p.ValidateResourceFn = func(t string, c *ResourceConfig) ([]string, []error) { - computed = c.IsComputed("foo") - return nil, nil - } - - c.Validate() - if !computed { - t.Fatal("should be computed") - } -} - -func TestContextValidate_varRefFilled(t *testing.T) { - m := testModule(t, "validate-variable-ref") - p := testProvider("aws") - c := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Variables: map[string]string{ - "foo": "bar", - }, - }) - - var value interface{} - p.ValidateResourceFn = func(t string, c *ResourceConfig) ([]string, []error) { - value, _ = c.Get("foo") - return nil, nil - } - - c.Validate() - if value != "bar" { - t.Fatalf("bad: %#v", value) - } -} - -func TestContextInput(t *testing.T) { - input := new(MockUIInput) - m := testModule(t, "input-vars") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Variables: map[string]string{ - "foo": "us-west-2", - "amis.us-east-1": "override", - }, - UIInput: input, - }) - - input.InputReturnMap = map[string]string{ - "var.foo": "us-east-1", - } - - if err := ctx.Input(InputModeStd); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformInputVarsStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextInput_provider(t *testing.T) { - m := testModule(t, "input-provider") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - var actual interface{} - p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { - c.Config["foo"] = "bar" - return c, nil - } - p.ConfigureFn = func(c *ResourceConfig) error { - actual = c.Config["foo"] - return nil - } - - if err := ctx.Input(InputModeStd); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Apply(); err != nil { - t.Fatalf("err: %s", err) - } - - if !reflect.DeepEqual(actual, "bar") { - t.Fatalf("bad: %#v", actual) - } -} - -func TestContextInput_providerId(t *testing.T) { - input := new(MockUIInput) - m := testModule(t, "input-provider") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - UIInput: input, - }) - - var actual interface{} - p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { - v, err := i.Input(&InputOpts{Id: "foo"}) - if err != nil { - return nil, err - } - - c.Config["foo"] = v - return c, nil - } - p.ConfigureFn = func(c *ResourceConfig) error { - actual = c.Config["foo"] - return nil - } - - input.InputReturnMap = map[string]string{ - "provider.aws.foo": "bar", - } - - if err := ctx.Input(InputModeStd); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Apply(); err != nil { - t.Fatalf("err: %s", err) - } - - if !reflect.DeepEqual(actual, "bar") { - t.Fatalf("bad: %#v", actual) - } -} - -func TestContextInput_providerOnly(t *testing.T) { - input := new(MockUIInput) - m := testModule(t, "input-provider-vars") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Variables: map[string]string{ - "foo": "us-west-2", - }, - UIInput: input, - }) - - input.InputReturnMap = map[string]string{ - "var.foo": "us-east-1", - } - - var actual interface{} - p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { - c.Config["foo"] = "bar" - return c, nil - } - p.ConfigureFn = func(c *ResourceConfig) error { - actual = c.Config["foo"] - return nil - } - - if err := ctx.Input(InputModeProvider); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - if !reflect.DeepEqual(actual, "bar") { - t.Fatalf("bad: %#v", actual) - } - - actualStr := strings.TrimSpace(state.String()) - expectedStr := strings.TrimSpace(testTerraformInputProviderOnlyStr) - if actualStr != expectedStr { - t.Fatalf("bad: \n%s", actualStr) - } -} - -func TestContextInput_providerVars(t *testing.T) { - input := new(MockUIInput) - m := testModule(t, "input-provider-with-vars") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Variables: map[string]string{ - "foo": "bar", - }, - UIInput: input, - }) - - input.InputReturnMap = map[string]string{ - "var.foo": "bar", - } - - var actual interface{} - p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { - c.Config["bar"] = "baz" - return c, nil - } - p.ConfigureFn = func(c *ResourceConfig) error { - actual, _ = c.Get("foo") - return nil - } - - if err := ctx.Input(InputModeStd); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Apply(); err != nil { - t.Fatalf("err: %s", err) - } - - if !reflect.DeepEqual(actual, "bar") { - t.Fatalf("bad: %#v", actual) - } -} - -func TestContextInput_varOnly(t *testing.T) { - input := new(MockUIInput) - m := testModule(t, "input-provider-vars") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Variables: map[string]string{ - "foo": "us-west-2", - }, - UIInput: input, - }) - - input.InputReturnMap = map[string]string{ - "var.foo": "us-east-1", - } - - var actual interface{} - p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { - c.Raw["foo"] = "bar" - return c, nil - } - p.ConfigureFn = func(c *ResourceConfig) error { - actual = c.Raw["foo"] - return nil - } - - if err := ctx.Input(InputModeVar); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - if reflect.DeepEqual(actual, "bar") { - t.Fatalf("bad: %#v", actual) - } - - actualStr := strings.TrimSpace(state.String()) - expectedStr := strings.TrimSpace(testTerraformInputVarOnlyStr) - if actualStr != expectedStr { - t.Fatalf("bad: \n%s", actualStr) - } -} - -func TestContextApply(t *testing.T) { - m := testModule(t, "apply-good") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - mod := state.RootModule() - if len(mod.Resources) < 2 { - t.Fatalf("bad: %#v", mod.Resources) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_emptyModule(t *testing.T) { - m := testModule(t, "apply-empty-module") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - actual = strings.Replace(actual, " ", "", -1) - expected := strings.TrimSpace(testTerraformApplyEmptyModuleStr) - if actual != expected { - t.Fatalf("bad: \n%s\nexpect:\n%s", actual, expected) - } -} - -func TestContextApply_createBeforeDestroy(t *testing.T) { - m := testModule(t, "apply-good-create-before") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "require_new": "abc", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - mod := state.RootModule() - if len(mod.Resources) != 1 { - t.Fatalf("bad: %#v", mod.Resources) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_Minimal(t *testing.T) { - m := testModule(t, "apply-minimal") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyMinimalStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_badDiff(t *testing.T) { - m := testModule(t, "apply-good") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { - return &InstanceDiff{ - Attributes: map[string]*ResourceAttrDiff{ - "newp": nil, - }, - }, nil - } - - if _, err := ctx.Apply(); err == nil { - t.Fatal("should error") - } -} - -func TestContextApply_cancel(t *testing.T) { - stopped := false - - m := testModule(t, "apply-cancel") - p := testProvider("aws") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) { - if !stopped { - stopped = true - go ctx.Stop() - - for { - if ctx.sh.Stopped() { - break - } - } - } - - return &InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "num": "2", - }, - }, nil - } - p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { - return &InstanceDiff{ - Attributes: map[string]*ResourceAttrDiff{ - "num": &ResourceAttrDiff{ - New: "bar", - }, - }, - }, nil - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - // Start the Apply in a goroutine - stateCh := make(chan *State) - go func() { - state, err := ctx.Apply() - if err != nil { - panic(err) - } - - stateCh <- state - }() - - state := <-stateCh - - mod := state.RootModule() - if len(mod.Resources) != 1 { - t.Fatalf("bad: %#v", mod.Resources) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyCancelStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_compute(t *testing.T) { - m := testModule(t, "apply-compute") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - ctx.variables = map[string]string{"value": "1"} - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyComputeStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_countDecrease(t *testing.T) { - m := testModule(t, "apply-count-dec") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "foo", - "type": "aws_instance", - }, - }, - }, - "aws_instance.foo.1": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "foo", - "type": "aws_instance", - }, - }, - }, - "aws_instance.foo.2": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "foo", - "type": "aws_instance", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyCountDecStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -/* -func TestContextApply_countDecreaseToOne(t *testing.T) { - m := testModule(t, "apply-count-dec-one") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "foo", - "type": "aws_instance", - }, - }, - }, - "aws_instance.foo.1": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - "aws_instance.foo.2": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyCountDecToOneStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} -*/ - -func TestContextApply_countTainted(t *testing.T) { - m := testModule(t, "apply-count-tainted") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo.0": &ResourceState{ - Type: "aws_instance", - Tainted: []*InstanceState{ - &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "foo", - "type": "aws_instance", - }, - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyCountTaintedStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_countVariable(t *testing.T) { - m := testModule(t, "apply-count-variable") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyCountVariableStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_module(t *testing.T) { - m := testModule(t, "apply-module") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyModuleStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_nilDiff(t *testing.T) { - m := testModule(t, "apply-good") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { - return nil, nil - } - - if _, err := ctx.Apply(); err == nil { - t.Fatal("should error") - } -} - -func TestContextApply_Provisioner_compute(t *testing.T) { - m := testModule(t, "apply-provisioner-compute") - p := testProvider("aws") - pr := testProvisioner() - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { - val, ok := c.Config["foo"] - if !ok || val != "computed_dynamical" { - t.Fatalf("bad value for foo: %v %#v", val, c) - } - - return nil - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - Variables: map[string]string{ - "value": "1", - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyProvisionerStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } - - // Verify apply was invoked - if !pr.ApplyCalled { - t.Fatalf("provisioner not invoked") - } -} - -func TestContextApply_provisionerCreateFail(t *testing.T) { - m := testModule(t, "apply-provisioner-fail-create") - p := testProvider("aws") - pr := testProvisioner() - p.DiffFn = testDiffFn - - p.ApplyFn = func( - info *InstanceInfo, - is *InstanceState, - id *InstanceDiff) (*InstanceState, error) { - is.ID = "foo" - return is, fmt.Errorf("error") - } - - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err == nil { - t.Fatal("should error") - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_provisionerCreateFailNoId(t *testing.T) { - m := testModule(t, "apply-provisioner-fail-create") - p := testProvider("aws") - pr := testProvisioner() - p.DiffFn = testDiffFn - - p.ApplyFn = func( - info *InstanceInfo, - is *InstanceState, - id *InstanceDiff) (*InstanceState, error) { - return nil, fmt.Errorf("error") - } - - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err == nil { - t.Fatal("should error") - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateNoIdStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_provisionerFail(t *testing.T) { - m := testModule(t, "apply-provisioner-fail") - p := testProvider("aws") - pr := testProvisioner() - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - - pr.ApplyFn = func(*InstanceState, *ResourceConfig) error { - return fmt.Errorf("EXPLOSION") - } - - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - Variables: map[string]string{ - "value": "1", - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err == nil { - t.Fatal("should error") - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyProvisionerFailStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_provisionerFail_createBeforeDestroy(t *testing.T) { - m := testModule(t, "apply-provisioner-fail-create-before") - p := testProvider("aws") - pr := testProvisioner() - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - pr.ApplyFn = func(*InstanceState, *ResourceConfig) error { - return fmt.Errorf("EXPLOSION") - } - - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "require_new": "abc", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - State: state, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err == nil { - t.Fatal("should error") - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateBeforeDestroyStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_error_createBeforeDestroy(t *testing.T) { - m := testModule(t, "apply-error-create-before") - p := testProvider("aws") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "require_new": "abc", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { - return nil, fmt.Errorf("error") - } - p.DiffFn = testDiffFn - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err == nil { - t.Fatal("should have error") - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyErrorCreateBeforeDestroyStr) - if actual != expected { - t.Fatalf("bad: \n%s\n\n\n%s", actual, expected) - } -} - -func TestContextApply_errorDestroy_createBeforeDestroy(t *testing.T) { - m := testModule(t, "apply-error-create-before") - p := testProvider("aws") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "require_new": "abc", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { - // Fail the destroy! - if id.Destroy { - return is, fmt.Errorf("error") - } - - // Create should work - is = &InstanceState{ - ID: "foo", - } - return is, nil - } - p.DiffFn = testDiffFn - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err == nil { - t.Fatal("should have error") - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyErrorDestroyCreateBeforeDestroyStr) - if actual != expected { - t.Fatalf("bad: actual:\n%s\n\nexpected:\n%s", actual, expected) - } -} - -func TestContextApply_provisionerResourceRef(t *testing.T) { - m := testModule(t, "apply-provisioner-resource-ref") - p := testProvider("aws") - pr := testProvisioner() - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { - val, ok := c.Config["foo"] - if !ok || val != "2" { - t.Fatalf("bad value for foo: %v %#v", val, c) - } - - return nil - } - - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyProvisionerResourceRefStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } - - // Verify apply was invoked - if !pr.ApplyCalled { - t.Fatalf("provisioner not invoked") - } -} - -// Provisioner should NOT run on a diff, only create -func TestContextApply_Provisioner_Diff(t *testing.T) { - m := testModule(t, "apply-provisioner-diff") - p := testProvider("aws") - pr := testProvisioner() - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { - return nil - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyProvisionerDiffStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } - - // Verify apply was invoked - if !pr.ApplyCalled { - t.Fatalf("provisioner not invoked") - } - pr.ApplyCalled = false - - // Change the state to force a diff - mod := state.RootModule() - mod.Resources["aws_instance.bar"].Primary.Attributes["foo"] = "baz" - - // Re-create context with state - ctx = testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - State: state, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state2, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual = strings.TrimSpace(state2.String()) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } - - // Verify apply was NOT invoked - if pr.ApplyCalled { - t.Fatalf("provisioner invoked") - } -} - -func TestContextApply_outputDiffVars(t *testing.T) { - m := testModule(t, "apply-good") - p := testProvider("aws") - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.baz": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { - for k, ad := range d.Attributes { - if ad.NewComputed { - return nil, fmt.Errorf("%s: computed", k) - } - } - - result := s.MergeDiff(d) - result.ID = "foo" - return result, nil - } - p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { - return &InstanceDiff{ - Attributes: map[string]*ResourceAttrDiff{ - "foo": &ResourceAttrDiff{ - NewComputed: true, - Type: DiffAttrOutput, - }, - "bar": &ResourceAttrDiff{ - New: "baz", - }, - }, - }, nil - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - if _, err := ctx.Apply(); err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestContextApply_Provisioner_ConnInfo(t *testing.T) { - m := testModule(t, "apply-provisioner-conninfo") - p := testProvider("aws") - pr := testProvisioner() - - p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { - if s.Ephemeral.ConnInfo == nil { - t.Fatalf("ConnInfo not initialized") - } - - result, _ := testApplyFn(info, s, d) - result.Ephemeral.ConnInfo = map[string]string{ - "type": "ssh", - "host": "127.0.0.1", - "port": "22", - } - return result, nil - } - p.DiffFn = testDiffFn - - pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { - conn := rs.Ephemeral.ConnInfo - if conn["type"] != "telnet" { - t.Fatalf("Bad: %#v", conn) - } - if conn["host"] != "127.0.0.1" { - t.Fatalf("Bad: %#v", conn) - } - if conn["port"] != "2222" { - t.Fatalf("Bad: %#v", conn) - } - if conn["user"] != "superuser" { - t.Fatalf("Bad: %#v", conn) - } - if conn["pass"] != "test" { - t.Fatalf("Bad: %#v", conn) - } - - return nil - } - - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - Variables: map[string]string{ - "value": "1", - "pass": "test", - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyProvisionerStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } - - // Verify apply was invoked - if !pr.ApplyCalled { - t.Fatalf("provisioner not invoked") - } -} - -func TestContextApply_destroy(t *testing.T) { - m := testModule(t, "apply-destroy") - h := new(HookRecordApplyOrder) - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Hooks: []Hook{h}, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - // First plan and apply a create operation - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Apply(); err != nil { - t.Fatalf("err: %s", err) - } - - // Next, plan and apply a destroy operation - if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { - t.Fatalf("err: %s", err) - } - - h.Active = true - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - // Test that things were destroyed - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyDestroyStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } - - // Test that things were destroyed _in the right order_ - expected2 := []string{"aws_instance.bar", "aws_instance.foo"} - actual2 := h.IDs - if !reflect.DeepEqual(actual2, expected2) { - t.Fatalf("bad: %#v", actual2) - } -} - -func TestContextApply_destroyOutputs(t *testing.T) { - m := testModule(t, "apply-destroy-outputs") - h := new(HookRecordApplyOrder) - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Hooks: []Hook{h}, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - // First plan and apply a create operation - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Apply(); err != nil { - t.Fatalf("err: %s", err) - } - - // Next, plan and apply a destroy operation - if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { - t.Fatalf("err: %s", err) - } - - h.Active = true - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - mod := state.RootModule() - if len(mod.Resources) > 0 { - t.Fatalf("bad: %#v", mod) - } -} - -func TestContextApply_destroyOrphan(t *testing.T) { - m := testModule(t, "apply-error") - p := testProvider("aws") - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.baz": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { - if d.Destroy { - return nil, nil - } - - result := s.MergeDiff(d) - result.ID = "foo" - return result, nil - } - p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { - return &InstanceDiff{ - Attributes: map[string]*ResourceAttrDiff{ - "num": &ResourceAttrDiff{ - New: "bar", - }, - }, - }, nil - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - mod := state.RootModule() - if _, ok := mod.Resources["aws_instance.baz"]; ok { - t.Fatalf("bad: %#v", mod.Resources) - } -} - -func TestContextApply_destroyTaintedProvisioner(t *testing.T) { - m := testModule(t, "apply-destroy-provisioner") - p := testProvider("aws") - pr := testProvisioner() - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - - called := false - pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { - called = true - return nil - } - - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Tainted: []*InstanceState{ - &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - }, - }, - }, - }, - }, - }, - }, - } - - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Provisioners: map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), - }, - State: s, - }) - - if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - if called { - t.Fatal("provisioner should not be called") - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace("") - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -/* -func TestContextApply_error(t *testing.T) { - errored := false - - m := testModule(t, "apply-error") - p := testProvider("aws") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) { - if errored { - state := &InstanceState{ - ID: "bar", - } - return state, fmt.Errorf("error") - } - errored = true - - return &InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "num": "2", - }, - }, nil - } - p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { - return &InstanceDiff{ - Attributes: map[string]*ResourceAttrDiff{ - "num": &ResourceAttrDiff{ - New: "bar", - }, - }, - }, nil - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err == nil { - t.Fatal("should have error") - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyErrorStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_errorPartial(t *testing.T) { - errored := false - - m := testModule(t, "apply-error") - p := testProvider("aws") - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { - if errored { - return s, fmt.Errorf("error") - } - errored = true - - return &InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "num": "2", - }, - }, nil - } - p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { - return &InstanceDiff{ - Attributes: map[string]*ResourceAttrDiff{ - "num": &ResourceAttrDiff{ - New: "bar", - }, - }, - }, nil - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err == nil { - t.Fatal("should have error") - } - - mod := state.RootModule() - if len(mod.Resources) != 2 { - t.Fatalf("bad: %#v", mod.Resources) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyErrorPartialStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} -*/ - -func TestContextApply_hook(t *testing.T) { - m := testModule(t, "apply-good") - h := new(MockHook) - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Hooks: []Hook{h}, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - if _, err := ctx.Apply(); err != nil { - t.Fatalf("err: %s", err) - } - - if !h.PreApplyCalled { - t.Fatal("should be called") - } - if !h.PostApplyCalled { - t.Fatal("should be called") - } -} - -func TestContextApply_idAttr(t *testing.T) { - m := testModule(t, "apply-idattr") - p := testProvider("aws") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { - result := s.MergeDiff(d) - result.ID = "foo" - result.Attributes = map[string]string{ - "id": "bar", - } - - return result, nil - } - p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { - return &InstanceDiff{ - Attributes: map[string]*ResourceAttrDiff{ - "num": &ResourceAttrDiff{ - New: "bar", - }, - }, - }, nil - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - mod := state.RootModule() - rs, ok := mod.Resources["aws_instance.foo"] - if !ok { - t.Fatal("not in state") - } - if rs.Primary.ID != "foo" { - t.Fatalf("bad: %#v", rs.Primary.ID) - } - if rs.Primary.Attributes["id"] != "foo" { - t.Fatalf("bad: %#v", rs.Primary.Attributes) - } -} - -func TestContextApply_output(t *testing.T) { - m := testModule(t, "apply-output") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyOutputStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_outputInvalid(t *testing.T) { - m := testModule(t, "apply-output-invalid") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - _, err := ctx.Plan(nil) - if err == nil { - t.Fatalf("err: %s", err) - } - if !strings.Contains(err.Error(), "is not a string") { - t.Fatalf("err: %s", err) - } -} - -func TestContextApply_outputList(t *testing.T) { - m := testModule(t, "apply-output-list") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyOutputListStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_outputMulti(t *testing.T) { - m := testModule(t, "apply-output-multi") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyOutputMultiStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_outputMultiIndex(t *testing.T) { - m := testModule(t, "apply-output-multi-index") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyOutputMultiIndexStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_taint(t *testing.T) { - m := testModule(t, "apply-taint") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Tainted: []*InstanceState{ - &InstanceState{ - ID: "baz", - Attributes: map[string]string{ - "num": "2", - "type": "aws_instance", - }, - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyTaintStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -/* -func TestContextApply_unknownAttribute(t *testing.T) { - m := testModule(t, "apply-unknown") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err == nil { - t.Fatal("should error") - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyUnknownAttrStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} -*/ - -func TestContextApply_vars(t *testing.T) { - m := testModule(t, "apply-vars") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Variables: map[string]string{ - "foo": "us-west-2", - "amis.us-east-1": "override", - }, - }) - - w, e := ctx.Validate() - if len(w) > 0 { - t.Fatalf("bad: %#v", w) - } - if len(e) > 0 { - t.Fatalf("bad: %s", e) - } - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyVarsStr) - if actual != expected { - t.Fatalf("bad: \n%s", actual) - } -} - -func TestContextApply_createBefore_depends(t *testing.T) { - m := testModule(t, "apply-depends-create-before") - h := new(HookRecordApplyOrder) - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "require_new": "ami-old", - }, - }, - }, - "aws_instance.lb": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "baz", - Attributes: map[string]string{ - "instance": "bar", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Hooks: []Hook{h}, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - h.Active = true - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - mod := state.RootModule() - if len(mod.Resources) < 2 { - t.Fatalf("bad: %#v", mod.Resources) - } - - actual := strings.TrimSpace(state.String()) - expected := strings.TrimSpace(testTerraformApplyDependsCreateBeforeStr) - if actual != expected { - t.Fatalf("bad: \n%s\n%s", actual, expected) - } - - // Test that things were managed _in the right order_ - order := h.States - diffs := h.Diffs - if order[0].ID != "" || diffs[0].Destroy { - t.Fatalf("should create new instance first: %#v", order) - } - - if order[1].ID != "baz" { - t.Fatalf("update must happen after create: %#v", order) - } - - if order[2].ID != "bar" || !diffs[2].Destroy { - t.Fatalf("destroy must happen after update: %#v", order) - } -} - -func TestContextApply_singleDestroy(t *testing.T) { - m := testModule(t, "apply-depends-create-before") - h := new(HookRecordApplyOrder) - p := testProvider("aws") - - invokeCount := 0 - p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { - invokeCount++ - switch invokeCount { - case 1: - if d.Destroy { - t.Fatalf("should not destroy") - } - if s.ID != "" { - t.Fatalf("should not have ID") - } - case 2: - if d.Destroy { - t.Fatalf("should not destroy") - } - if s.ID != "baz" { - t.Fatalf("should have id") - } - case 3: - if !d.Destroy { - t.Fatalf("should destroy") - } - if s.ID == "" { - t.Fatalf("should have ID") - } - default: - t.Fatalf("bad invoke count %d", invokeCount) - } - return testApplyFn(info, s, d) - } - p.DiffFn = testDiffFn - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "require_new": "ami-old", - }, - }, - }, - "aws_instance.lb": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "baz", - Attributes: map[string]string{ - "instance": "bar", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Hooks: []Hook{h}, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - h.Active = true - state, err := ctx.Apply() - if err != nil { - t.Fatalf("err: %s", err) - } - - if invokeCount != 3 { - t.Fatalf("bad: %d", invokeCount) - } -} - -func TestContextPlan(t *testing.T) { - m := testModule(t, "plan-good") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if len(plan.Diff.RootModule().Resources) < 2 { - t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_emptyDiff(t *testing.T) { - m := testModule(t, "plan-empty") - p := testProvider("aws") - p.DiffFn = func( - info *InstanceInfo, - s *InstanceState, - c *ResourceConfig) (*InstanceDiff, error) { - return nil, nil - } - - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanEmptyStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_minimal(t *testing.T) { - m := testModule(t, "plan-empty") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanEmptyStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_modules(t *testing.T) { - m := testModule(t, "plan-modules") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModulesStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_moduleInput(t *testing.T) { - m := testModule(t, "plan-module-input") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModuleInputStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_moduleInputComputed(t *testing.T) { - m := testModule(t, "plan-module-input-computed") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModuleInputComputedStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_moduleInputFromVar(t *testing.T) { - m := testModule(t, "plan-module-input-var") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Variables: map[string]string{ - "foo": "52", - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModuleInputVarStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} -func TestContextPlan_moduleMultiVar(t *testing.T) { - m := testModule(t, "plan-module-multi-var") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModuleMultiVarStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} -func TestContextPlan_moduleOrphans(t *testing.T) { - m := testModule(t, "plan-modules-remove") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root", "child"}, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "baz", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModuleOrphansStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_moduleProviderInherit(t *testing.T) { - var l sync.Mutex - var calls []string - - m := testModule(t, "plan-module-provider-inherit") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": func() (ResourceProvider, error) { - l.Lock() - defer l.Unlock() - - p := testProvider("aws") - p.ConfigureFn = func(c *ResourceConfig) error { - if v, ok := c.Get("from"); !ok || v.(string) != "root" { - return fmt.Errorf("bad") - } - - return nil - } - p.DiffFn = func( - info *InstanceInfo, - state *InstanceState, - c *ResourceConfig) (*InstanceDiff, error) { - v, _ := c.Get("from") - calls = append(calls, v.(string)) - return testDiffFn(info, state, c) - } - return p, nil - }, - }, - }) - - _, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := calls - sort.Strings(actual) - expected := []string{"child", "root"} - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestContextPlan_moduleProviderDefaults(t *testing.T) { - var l sync.Mutex - var calls []string - toCount := 0 - - m := testModule(t, "plan-module-provider-defaults") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": func() (ResourceProvider, error) { - l.Lock() - defer l.Unlock() - - p := testProvider("aws") - p.ConfigureFn = func(c *ResourceConfig) error { - if v, ok := c.Get("from"); !ok || v.(string) != "root" { - return fmt.Errorf("bad") - } - if v, ok := c.Get("to"); ok && v.(string) == "child" { - toCount++ - } - - return nil - } - p.DiffFn = func( - info *InstanceInfo, - state *InstanceState, - c *ResourceConfig) (*InstanceDiff, error) { - v, _ := c.Get("from") - calls = append(calls, v.(string)) - return testDiffFn(info, state, c) - } - return p, nil - }, - }, - }) - - _, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if toCount != 1 { - t.Fatal("provider in child didn't set proper config") - } - - actual := calls - sort.Strings(actual) - expected := []string{"child", "root"} - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestContextPlan_moduleProviderDefaultsVar(t *testing.T) { - var l sync.Mutex - var calls []string - - m := testModule(t, "plan-module-provider-defaults-var") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": func() (ResourceProvider, error) { - l.Lock() - defer l.Unlock() - - p := testProvider("aws") - p.ConfigureFn = func(c *ResourceConfig) error { - var buf bytes.Buffer - if v, ok := c.Get("from"); ok { - buf.WriteString(v.(string) + "\n") - } - if v, ok := c.Get("to"); ok { - buf.WriteString(v.(string) + "\n") - } - - calls = append(calls, buf.String()) - return nil - } - p.DiffFn = testDiffFn - return p, nil - }, - }, - Variables: map[string]string{ - "foo": "root", - }, - }) - - _, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := []string{ - "root\n", - "root\nchild\n", - } - if !reflect.DeepEqual(calls, expected) { - t.Fatalf("BAD: %#v", calls) - } -} - -func TestContextPlan_moduleVar(t *testing.T) { - m := testModule(t, "plan-module-var") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModuleVarStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_moduleVarComputed(t *testing.T) { - m := testModule(t, "plan-module-var-computed") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModuleVarComputedStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_nil(t *testing.T) { - m := testModule(t, "plan-nil") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if len(plan.Diff.RootModule().Resources) != 0 { - t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) - } -} - -func TestContextPlan_computed(t *testing.T) { - m := testModule(t, "plan-computed") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanComputedStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_computedList(t *testing.T) { - m := testModule(t, "plan-computed-list") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanComputedListStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_count(t *testing.T) { - m := testModule(t, "plan-count") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if len(plan.Diff.RootModule().Resources) < 6 { - t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanCountStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_countComputed(t *testing.T) { - m := testModule(t, "plan-count-computed") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - _, err := ctx.Plan(nil) - if err == nil { - t.Fatal("should error") - } -} - -func TestContextPlan_countIndex(t *testing.T) { - m := testModule(t, "plan-count-index") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanCountIndexStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_countIndexZero(t *testing.T) { - m := testModule(t, "plan-count-index-zero") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanCountIndexZeroStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_countVar(t *testing.T) { - m := testModule(t, "plan-count-var") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Variables: map[string]string{ - "count": "3", - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanCountVarStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_countZero(t *testing.T) { - m := testModule(t, "plan-count-zero") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanCountZeroStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_countOneIndex(t *testing.T) { - m := testModule(t, "plan-count-one-index") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanCountOneIndexStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_countDecreaseToOne(t *testing.T) { - m := testModule(t, "plan-count-dec") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "foo", - "type": "aws_instance", - }, - }, - }, - "aws_instance.foo.1": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - "aws_instance.foo.2": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanCountDecreaseStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_countIncreaseFromNotSet(t *testing.T) { - m := testModule(t, "plan-count-inc") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "foo", - "type": "aws_instance", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanCountIncreaseStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_countIncreaseFromOne(t *testing.T) { - m := testModule(t, "plan-count-inc") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "foo", - "type": "aws_instance", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanCountIncreaseFromOneStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_destroy(t *testing.T) { - m := testModule(t, "plan-destroy") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.one": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - "aws_instance.two": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "baz", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(&PlanOpts{Destroy: true}) - if err != nil { - t.Fatalf("err: %s", err) - } - - if len(plan.Diff.RootModule().Resources) != 2 { - t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanDestroyStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_moduleDestroy(t *testing.T) { - m := testModule(t, "plan-module-destroy") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - &ModuleState{ - Path: []string{"root", "child"}, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(&PlanOpts{Destroy: true}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModuleDestroyStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_moduleDestroyMultivar(t *testing.T) { - m := testModule(t, "plan-module-destroy-multivar") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{}, - }, - &ModuleState{ - Path: []string{"root", "child"}, - Resources: map[string]*ResourceState{ - "aws_instance.foo.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar0", - }, - }, - "aws_instance.foo.1": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar1", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(&PlanOpts{Destroy: true}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanModuleDestroyMultivarStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_pathVar(t *testing.T) { - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("err: %s", err) - } - - m := testModule(t, "plan-path-var") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanPathVarStr) - - module := m.Config().Dir - root := m.Config().Dir - if runtime.GOOS == "windows" { - // The attributes in the diff are %#v-formatted. This means - // that all `\` characters in the Windows paths are escaped - // with a `\`. We need to escape the `\` characters in cwd, - // module, and root before doing any comparison work. - cwd = strings.Replace(cwd, `\`, `\\`, -1) - module = strings.Replace(module, `\`, `\\`, -1) - root = strings.Replace(root, `\`, `\\`, -1) - } - - // Warning: this ordering REALLY matters for this test. The - // order is: cwd, module, root. - expected = fmt.Sprintf(expected, cwd, module, root) - - if actual != expected { - t.Fatalf("bad:\n%s\n\nexpected:\n\n%s", actual, expected) - } -} - -func TestContextPlan_diffVar(t *testing.T) { - m := testModule(t, "plan-diffvar") - p := testProvider("aws") - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "num": "2", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - p.DiffFn = func( - info *InstanceInfo, - s *InstanceState, - c *ResourceConfig) (*InstanceDiff, error) { - if s.ID != "bar" { - return testDiffFn(info, s, c) - } - - return &InstanceDiff{ - Attributes: map[string]*ResourceAttrDiff{ - "num": &ResourceAttrDiff{ - Old: "2", - New: "3", - }, - }, - }, nil - } - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanDiffVarStr) - if actual != expected { - t.Fatalf("actual:\n%s\n\nexpected:\n%s", actual, expected) - } -} - -func TestContextPlan_hook(t *testing.T) { - m := testModule(t, "plan-good") - h := new(MockHook) - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Hooks: []Hook{h}, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - _, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !h.PreDiffCalled { - t.Fatal("should be called") - } - if !h.PostDiffCalled { - t.Fatal("should be called") - } -} - -func TestContextPlan_orphan(t *testing.T) { - m := testModule(t, "plan-orphan") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.baz": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanOrphanStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_state(t *testing.T) { - m := testModule(t, "plan-good") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if len(plan.Diff.RootModule().Resources) < 2 { - t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanStateStr) - if actual != expected { - t.Fatalf("bad:\n%s\n\nexpected:\n\n%s", actual, expected) - } -} - -func TestContextPlan_taint(t *testing.T) { - m := testModule(t, "plan-taint") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{"num": "2"}, - }, - }, - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Tainted: []*InstanceState{ - &InstanceState{ - ID: "baz", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanTaintStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -// Doing a Refresh (or any operation really, but Refresh usually -// happens first) with a config with an unknown provider should result in -// an error. The key bug this found was that this wasn't happening if -// Providers was _empty_. -func TestContextRefresh_unknownProvider(t *testing.T) { - m := testModule(t, "refresh-unknown-provider") - p := testProvider("aws") - p.ApplyFn = testApplyFn - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{}, - }) - - if _, err := ctx.Refresh(); err == nil { - t.Fatal("should error") - } -} - -func TestContextPlan_multiple_taint(t *testing.T) { - m := testModule(t, "plan-taint") - p := testProvider("aws") - p.DiffFn = testDiffFn - s := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{"num": "2"}, - }, - }, - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Tainted: []*InstanceState{ - &InstanceState{ - ID: "baz", - }, - &InstanceState{ - ID: "zip", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: s, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanMultipleTaintStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_provider(t *testing.T) { - m := testModule(t, "plan-provider") - p := testProvider("aws") - p.DiffFn = testDiffFn - - var value interface{} - p.ConfigureFn = func(c *ResourceConfig) error { - value, _ = c.Get("foo") - return nil - } - - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - Variables: map[string]string{ - "foo": "bar", - }, - }) - - if _, err := ctx.Plan(nil); err != nil { - t.Fatalf("err: %s", err) - } - - if value != "bar" { - t.Fatalf("bad: %#v", value) - } -} - -func TestContextPlan_varMultiCountOne(t *testing.T) { - m := testModule(t, "plan-var-multi-count-one") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - plan, err := ctx.Plan(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(plan.String()) - expected := strings.TrimSpace(testTerraformPlanVarMultiCountOneStr) - if actual != expected { - t.Fatalf("bad:\n%s", actual) - } -} - -func TestContextPlan_varListErr(t *testing.T) { - m := testModule(t, "plan-var-list-err") - p := testProvider("aws") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - _, err := ctx.Plan(nil) - if err == nil { - t.Fatal("should error") - } -} - -func TestContextRefresh(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-basic") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - }, - }) - - p.RefreshFn = nil - p.RefreshReturn = &InstanceState{ - ID: "foo", - } - - s, err := ctx.Refresh() - mod := s.RootModule() - if err != nil { - t.Fatalf("err: %s", err) - } - if !p.RefreshCalled { - t.Fatal("refresh should be called") - } - if p.RefreshState.ID != "foo" { - t.Fatalf("bad: %#v", p.RefreshState) - } - if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) { - t.Fatalf("bad: %#v %#v", mod.Resources["aws_instance.web"], p.RefreshReturn) - } - - for _, r := range mod.Resources { - if r.Type == "" { - t.Fatalf("no type: %#v", r) - } - } -} - -func TestContextRefresh_delete(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-basic") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - }, - }) - - p.RefreshFn = nil - p.RefreshReturn = nil - - s, err := ctx.Refresh() - if err != nil { - t.Fatalf("err: %s", err) - } - - mod := s.RootModule() - if len(mod.Resources) > 0 { - t.Fatal("resources should be empty") - } -} - -func TestContextRefresh_ignoreUncreated(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-basic") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: nil, - }) - - p.RefreshFn = nil - p.RefreshReturn = &InstanceState{ - ID: "foo", - } - - _, err := ctx.Refresh() - if err != nil { - t.Fatalf("err: %s", err) - } - if p.RefreshCalled { - t.Fatal("refresh should not be called") - } -} - -func TestContextRefresh_hook(t *testing.T) { - h := new(MockHook) - p := testProvider("aws") - m := testModule(t, "refresh-basic") - ctx := testContext(t, &ContextOpts{ - Module: m, - Hooks: []Hook{h}, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - }, - }) - - if _, err := ctx.Refresh(); err != nil { - t.Fatalf("err: %s", err) - } - if !h.PreRefreshCalled { - t.Fatal("should be called") - } - /* - TODO(mitchcellh): remove when we add InstanceInfo param - if h.PreRefreshState.Type != "aws_instance" { - t.Fatalf("bad: %#v", h.PreRefreshState) - } - */ - if !h.PostRefreshCalled { - t.Fatal("should be called") - } - /* - TODO(mitchcellh): remove when we add InstanceInfo param - if h.PostRefreshState.Type != "aws_instance" { - t.Fatalf("bad: %#v", h.PostRefreshState) - } - */ -} - -func TestContextRefresh_modules(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-modules") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Tainted: []*InstanceState{ - &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - - &ModuleState{ - Path: []string{"root", "child"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "baz", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - - p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) { - if s.ID != "baz" { - return s, nil - } - - s.ID = "new" - return s, nil - } - - s, err := ctx.Refresh() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(s.String()) - expected := strings.TrimSpace(testContextRefreshModuleStr) - if actual != expected { - t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) - } -} - -func TestContextRefresh_moduleInputComputedOutput(t *testing.T) { - m := testModule(t, "refresh-module-input-computed-output") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Refresh(); err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestContextRefresh_moduleVarModule(t *testing.T) { - m := testModule(t, "refresh-module-var-module") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Refresh(); err != nil { - t.Fatalf("err: %s", err) - } -} - -// GH-70 -func TestContextRefresh_noState(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-no-state") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - p.RefreshFn = nil - p.RefreshReturn = &InstanceState{ - ID: "foo", - } - - if _, err := ctx.Refresh(); err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestContextRefresh_outputPartial(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-output-partial") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - }, - }) - - p.RefreshFn = nil - p.RefreshReturn = nil - - s, err := ctx.Refresh() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(s.String()) - expected := strings.TrimSpace(testContextRefreshOutputPartialStr) - if actual != expected { - t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) - } -} - -func TestContextRefresh_state(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-basic") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - - p.RefreshFn = nil - p.RefreshReturn = &InstanceState{ - ID: "foo", - } - - s, err := ctx.Refresh() - if err != nil { - t.Fatalf("err: %s", err) - } - originalMod := state.RootModule() - mod := s.RootModule() - if !p.RefreshCalled { - t.Fatal("refresh should be called") - } - if !reflect.DeepEqual(p.RefreshState, originalMod.Resources["aws_instance.web"].Primary) { - t.Fatalf("bad: %#v %#v", p.RefreshState, originalMod.Resources["aws_instance.web"].Primary) - } - if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) { - t.Fatalf("bad: %#v", mod.Resources) - } -} - -func TestContextRefresh_tainted(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-basic") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Tainted: []*InstanceState{ - &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - }, - } - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: state, - }) - - p.RefreshFn = nil - p.RefreshReturn = &InstanceState{ - ID: "foo", - } - - s, err := ctx.Refresh() - if err != nil { - t.Fatalf("err: %s", err) - } - if !p.RefreshCalled { - t.Fatal("refresh should be called") - } - - actual := strings.TrimSpace(s.String()) - expected := strings.TrimSpace(testContextRefreshTaintedStr) - if actual != expected { - t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) - } -} - -func TestContextRefresh_vars(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-vars") - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - State: &State{ - - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - }, - }) - - p.RefreshFn = nil - p.RefreshReturn = &InstanceState{ - ID: "foo", - } - - s, err := ctx.Refresh() - if err != nil { - t.Fatalf("err: %s", err) - } - mod := s.RootModule() - if !p.RefreshCalled { - t.Fatal("refresh should be called") - } - if p.RefreshState.ID != "foo" { - t.Fatalf("bad: %#v", p.RefreshState) - } - if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) { - t.Fatalf("bad: %#v", mod.Resources["aws_instance.web"]) - } - - for _, r := range mod.Resources { - if r.Type == "" { - t.Fatalf("no type: %#v", r) - } - } -} - -func testContext(t *testing.T, opts *ContextOpts) *Context { - return NewContext(opts) -} - -func testApplyFn( - info *InstanceInfo, - s *InstanceState, - d *InstanceDiff) (*InstanceState, error) { - if d.Destroy { - return nil, nil - } - - id := "foo" - if idAttr, ok := d.Attributes["id"]; ok && !idAttr.NewComputed { - id = idAttr.New - } - - result := &InstanceState{ - ID: id, - } - - if d != nil { - result = result.MergeDiff(d) - } - return result, nil -} - -func testDiffFn( - info *InstanceInfo, - s *InstanceState, - c *ResourceConfig) (*InstanceDiff, error) { - var diff InstanceDiff - diff.Attributes = make(map[string]*ResourceAttrDiff) - - for k, v := range c.Raw { - if _, ok := v.(string); !ok { - continue - } - - if k == "nil" { - return nil, nil - } - - // This key is used for other purposes - if k == "compute_value" { - continue - } - - if k == "compute" { - attrDiff := &ResourceAttrDiff{ - Old: "", - New: "", - NewComputed: true, - } - - if cv, ok := c.Config["compute_value"]; ok { - if cv.(string) == "1" { - attrDiff.NewComputed = false - attrDiff.New = fmt.Sprintf("computed_%s", v.(string)) - } - } - - diff.Attributes[v.(string)] = attrDiff - continue - } - - // If this key is not computed, then look it up in the - // cleaned config. - found := false - for _, ck := range c.ComputedKeys { - if ck == k { - found = true - break - } - } - if !found { - v = c.Config[k] - } - - attrDiff := &ResourceAttrDiff{ - Old: "", - New: v.(string), - } - - if k == "require_new" { - attrDiff.RequiresNew = true - } - diff.Attributes[k] = attrDiff - } - - for _, k := range c.ComputedKeys { - diff.Attributes[k] = &ResourceAttrDiff{ - Old: "", - NewComputed: true, - } - } - - for k, v := range diff.Attributes { - if v.NewComputed { - continue - } - - old, ok := s.Attributes[k] - if !ok { - continue - } - if old == v.New { - delete(diff.Attributes, k) - } - } - - if !diff.Empty() { - diff.Attributes["type"] = &ResourceAttrDiff{ - Old: "", - New: info.Type, - } - } - - return &diff, nil -} - -func testProvider(prefix string) *MockResourceProvider { - p := new(MockResourceProvider) - p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) { - return s, nil - } - p.ResourcesReturn = []ResourceType{ - ResourceType{ - Name: fmt.Sprintf("%s_instance", prefix), - }, - } - - return p -} - -func testProvisioner() *MockResourceProvisioner { - p := new(MockResourceProvisioner) - return p -} - -const testContextGraph = ` -root: root -aws_instance.bar - aws_instance.bar -> provider.aws -aws_instance.foo - aws_instance.foo -> provider.aws -provider.aws -root - root -> aws_instance.bar - root -> aws_instance.foo -` - -const testContextRefreshModuleStr = ` -aws_instance.web: (1 tainted) - ID = - Tainted ID 1 = bar - -module.child: - aws_instance.web: - ID = new -` - -const testContextRefreshOutputPartialStr = ` - -` - -const testContextRefreshTaintedStr = ` -aws_instance.web: (1 tainted) - ID = - Tainted ID 1 = foo -` diff --git a/terraform/context_test.go b/terraform/context_test.go index bba98b291..aa844177a 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -4630,3 +4630,171 @@ func TestContext2Apply_singleDestroy(t *testing.T) { func testContext2(t *testing.T, opts *ContextOpts) *Context2 { return NewContext2(opts) } + +func testApplyFn( + info *InstanceInfo, + s *InstanceState, + d *InstanceDiff) (*InstanceState, error) { + if d.Destroy { + return nil, nil + } + + id := "foo" + if idAttr, ok := d.Attributes["id"]; ok && !idAttr.NewComputed { + id = idAttr.New + } + + result := &InstanceState{ + ID: id, + } + + if d != nil { + result = result.MergeDiff(d) + } + return result, nil +} + +func testDiffFn( + info *InstanceInfo, + s *InstanceState, + c *ResourceConfig) (*InstanceDiff, error) { + var diff InstanceDiff + diff.Attributes = make(map[string]*ResourceAttrDiff) + + for k, v := range c.Raw { + if _, ok := v.(string); !ok { + continue + } + + if k == "nil" { + return nil, nil + } + + // This key is used for other purposes + if k == "compute_value" { + continue + } + + if k == "compute" { + attrDiff := &ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + } + + if cv, ok := c.Config["compute_value"]; ok { + if cv.(string) == "1" { + attrDiff.NewComputed = false + attrDiff.New = fmt.Sprintf("computed_%s", v.(string)) + } + } + + diff.Attributes[v.(string)] = attrDiff + continue + } + + // If this key is not computed, then look it up in the + // cleaned config. + found := false + for _, ck := range c.ComputedKeys { + if ck == k { + found = true + break + } + } + if !found { + v = c.Config[k] + } + + attrDiff := &ResourceAttrDiff{ + Old: "", + New: v.(string), + } + + if k == "require_new" { + attrDiff.RequiresNew = true + } + diff.Attributes[k] = attrDiff + } + + for _, k := range c.ComputedKeys { + diff.Attributes[k] = &ResourceAttrDiff{ + Old: "", + NewComputed: true, + } + } + + for k, v := range diff.Attributes { + if v.NewComputed { + continue + } + + old, ok := s.Attributes[k] + if !ok { + continue + } + if old == v.New { + delete(diff.Attributes, k) + } + } + + if !diff.Empty() { + diff.Attributes["type"] = &ResourceAttrDiff{ + Old: "", + New: info.Type, + } + } + + return &diff, nil +} + +func testProvider(prefix string) *MockResourceProvider { + p := new(MockResourceProvider) + p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) { + return s, nil + } + p.ResourcesReturn = []ResourceType{ + ResourceType{ + Name: fmt.Sprintf("%s_instance", prefix), + }, + } + + return p +} + +func testProvisioner() *MockResourceProvisioner { + p := new(MockResourceProvisioner) + return p +} + +const testContextGraph = ` +root: root +aws_instance.bar + aws_instance.bar -> provider.aws +aws_instance.foo + aws_instance.foo -> provider.aws +provider.aws +root + root -> aws_instance.bar + root -> aws_instance.foo +` + +const testContextRefreshModuleStr = ` +aws_instance.web: (1 tainted) + ID = + Tainted ID 1 = bar + +module.child: + aws_instance.web: + ID = new +` + +const testContextRefreshOutputPartialStr = ` + +` + +const testContextRefreshTaintedStr = ` +aws_instance.web: (1 tainted) + ID = + Tainted ID 1 = foo +` diff --git a/terraform/plan.go b/terraform/plan.go index e73fde383..75ad06540 100644 --- a/terraform/plan.go +++ b/terraform/plan.go @@ -42,12 +42,12 @@ type Plan struct { // // The following fields in opts are overridden by the plan: Config, // Diff, State, Variables. -func (p *Plan) Context(opts *ContextOpts) *Context { +func (p *Plan) Context(opts *ContextOpts) *Context2 { opts.Diff = p.Diff opts.Module = p.Module opts.State = p.State opts.Variables = p.Vars - return NewContext(opts) + return NewContext2(opts) } func (p *Plan) String() string { diff --git a/terraform/resource.go b/terraform/resource.go index 931d1b323..7775ab789 100644 --- a/terraform/resource.go +++ b/terraform/resource.go @@ -93,7 +93,7 @@ type ResourceConfig struct { // NewResourceConfig creates a new ResourceConfig from a config.RawConfig. func NewResourceConfig(c *config.RawConfig) *ResourceConfig { result := &ResourceConfig{raw: c} - result.interpolate(nil, nil) + result.interpolateForce() return result } @@ -201,36 +201,18 @@ func (c *ResourceConfig) get( return current, true } -func (c *ResourceConfig) interpolate( - ctx *walkContext, r *Resource) error { - if c == nil { - return nil - } - - if ctx != nil { - if err := ctx.computeVars(c.raw, r); err != nil { - return err - } - } - - if c.raw == nil { - var err error - c.raw, err = config.NewRawConfig(make(map[string]interface{})) - if err != nil { - return err - } - } - - c.ComputedKeys = c.raw.UnknownKeys() - c.Raw = c.raw.Raw - c.Config = c.raw.Config() - return nil -} - // interpolateForce is a temporary thing. We want to get rid of interpolate // above and likewise this, but it can only be done after the f-ast-graph // refactor is complete. func (c *ResourceConfig) interpolateForce() { + if c.raw == nil { + var err error + c.raw, err = config.NewRawConfig(make(map[string]interface{})) + if err != nil { + panic(err) + } + } + c.ComputedKeys = c.raw.UnknownKeys() c.Raw = c.raw.Raw c.Config = c.raw.Config() diff --git a/terraform/resource_test.go b/terraform/resource_test.go index 5d9b09a40..4b23be0d1 100644 --- a/terraform/resource_test.go +++ b/terraform/resource_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/config/lang/ast" ) func TestInstanceInfo(t *testing.T) { @@ -99,17 +100,20 @@ func TestResourceConfigGet(t *testing.T) { } } - rc := NewResourceConfig(rawC) if tc.Vars != nil { - ctx := NewContext(&ContextOpts{Variables: tc.Vars}) - err := rc.interpolate( - ctx.walkContext(walkInvalid, rootModulePath), - nil) - if err != nil { + vs := make(map[string]ast.Variable) + for k, v := range tc.Vars { + vs["var."+k] = ast.Variable{Value: v, Type: ast.TypeString} + } + + if err := rawC.Interpolate(vs); err != nil { t.Fatalf("err: %s", err) } } + rc := NewResourceConfig(rawC) + rc.interpolateForce() + v, _ := rc.Get(tc.Key) if !reflect.DeepEqual(v, tc.Value) { t.Fatalf("%d bad: %#v", i, v)