8 "github.com/hashicorp/terraform/terraform"
11 // testStepConfig runs a config-mode test step
13 opts terraform.ContextOpts,
14 state *terraform.State,
15 step TestStep) (*terraform.State, error) {
16 return testStep(opts, state, step)
20 opts terraform.ContextOpts,
21 state *terraform.State,
22 step TestStep) (*terraform.State, error) {
23 mod, err := testModule(opts, step)
31 opts.Destroy = step.Destroy
32 ctx, err := terraform.NewContext(&opts)
34 return state, fmt.Errorf("Error initializing context: %s", err)
36 if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
38 estrs := make([]string, len(es))
39 for i, e := range es {
42 return state, fmt.Errorf(
43 "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v",
46 log.Printf("[WARN] Config warnings: %#v", ws)
50 state, err = ctx.Refresh()
52 return state, fmt.Errorf(
53 "Error refreshing: %s", err)
56 // If this step is a PlanOnly step, skip over this first Plan and subsequent
57 // Apply, and use the follow up Plan that checks for perpetual diffs
60 if p, err := ctx.Plan(); err != nil {
61 return state, fmt.Errorf(
62 "Error planning: %s", err)
64 log.Printf("[WARN] Test: Step plan: %s", p)
67 // We need to keep a copy of the state prior to destroying
68 // such that destroy steps can verify their behaviour in the check
70 stateBeforeApplication := state.DeepCopy()
73 state, err = ctx.Apply()
75 return state, fmt.Errorf("Error applying: %s", err)
79 if step.Check != nil {
81 if err := step.Check(stateBeforeApplication); err != nil {
82 return state, fmt.Errorf("Check failed: %s", err)
85 if err := step.Check(state); err != nil {
86 return state, fmt.Errorf("Check failed: %s", err)
92 // Now, verify that Plan is now empty and we don't have a perpetual diff issue
93 // We do this with TWO plans. One without a refresh.
95 if p, err = ctx.Plan(); err != nil {
96 return state, fmt.Errorf("Error on follow-up plan: %s", err)
98 if p.Diff != nil && !p.Diff.Empty() {
99 if step.ExpectNonEmptyPlan {
100 log.Printf("[INFO] Got non-empty plan, as expected:\n\n%s", p)
102 return state, fmt.Errorf(
103 "After applying this step, the plan was not empty:\n\n%s", p)
107 // And another after a Refresh.
108 if !step.Destroy || (step.Destroy && !step.PreventPostDestroyRefresh) {
109 state, err = ctx.Refresh()
111 return state, fmt.Errorf(
112 "Error on follow-up refresh: %s", err)
115 if p, err = ctx.Plan(); err != nil {
116 return state, fmt.Errorf("Error on second follow-up plan: %s", err)
118 empty := p.Diff == nil || p.Diff.Empty()
120 // Data resources are tricky because they legitimately get instantiated
121 // during refresh so that they will be already populated during the
122 // plan walk. Because of this, if we have any data resources in the
123 // config we'll end up wanting to destroy them again here. This is
124 // acceptable and expected, and we'll treat it as "empty" for the
125 // sake of this testing.
129 for _, moduleDiff := range p.Diff.Modules {
130 for k, instanceDiff := range moduleDiff.Resources {
131 if !strings.HasPrefix(k, "data.") {
136 if !instanceDiff.Destroy {
144 if step.ExpectNonEmptyPlan {
145 log.Printf("[INFO] Got non-empty plan, as expected:\n\n%s", p)
147 return state, fmt.Errorf(
148 "After applying this step and refreshing, "+
149 "the plan was not empty:\n\n%s", p)
153 // Made it here, but expected a non-empty plan, fail!
154 if step.ExpectNonEmptyPlan && (p.Diff == nil || p.Diff.Empty()) {
155 return state, fmt.Errorf("Expected a non-empty plan, but got an empty plan!")
158 // Made it here? Good job test step!