diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go new file mode 100644 index 0000000..537a11c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go | |||
@@ -0,0 +1,160 @@ | |||
1 | package resource | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "log" | ||
6 | "strings" | ||
7 | |||
8 | "github.com/hashicorp/terraform/terraform" | ||
9 | ) | ||
10 | |||
11 | // testStepConfig runs a config-mode test step | ||
12 | func testStepConfig( | ||
13 | opts terraform.ContextOpts, | ||
14 | state *terraform.State, | ||
15 | step TestStep) (*terraform.State, error) { | ||
16 | return testStep(opts, state, step) | ||
17 | } | ||
18 | |||
19 | func testStep( | ||
20 | opts terraform.ContextOpts, | ||
21 | state *terraform.State, | ||
22 | step TestStep) (*terraform.State, error) { | ||
23 | mod, err := testModule(opts, step) | ||
24 | if err != nil { | ||
25 | return state, err | ||
26 | } | ||
27 | |||
28 | // Build the context | ||
29 | opts.Module = mod | ||
30 | opts.State = state | ||
31 | opts.Destroy = step.Destroy | ||
32 | ctx, err := terraform.NewContext(&opts) | ||
33 | if err != nil { | ||
34 | return state, fmt.Errorf("Error initializing context: %s", err) | ||
35 | } | ||
36 | if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { | ||
37 | if len(es) > 0 { | ||
38 | estrs := make([]string, len(es)) | ||
39 | for i, e := range es { | ||
40 | estrs[i] = e.Error() | ||
41 | } | ||
42 | return state, fmt.Errorf( | ||
43 | "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v", | ||
44 | ws, estrs) | ||
45 | } | ||
46 | log.Printf("[WARN] Config warnings: %#v", ws) | ||
47 | } | ||
48 | |||
49 | // Refresh! | ||
50 | state, err = ctx.Refresh() | ||
51 | if err != nil { | ||
52 | return state, fmt.Errorf( | ||
53 | "Error refreshing: %s", err) | ||
54 | } | ||
55 | |||
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 | ||
58 | if !step.PlanOnly { | ||
59 | // Plan! | ||
60 | if p, err := ctx.Plan(); err != nil { | ||
61 | return state, fmt.Errorf( | ||
62 | "Error planning: %s", err) | ||
63 | } else { | ||
64 | log.Printf("[WARN] Test: Step plan: %s", p) | ||
65 | } | ||
66 | |||
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 | ||
69 | // function | ||
70 | stateBeforeApplication := state.DeepCopy() | ||
71 | |||
72 | // Apply! | ||
73 | state, err = ctx.Apply() | ||
74 | if err != nil { | ||
75 | return state, fmt.Errorf("Error applying: %s", err) | ||
76 | } | ||
77 | |||
78 | // Check! Excitement! | ||
79 | if step.Check != nil { | ||
80 | if step.Destroy { | ||
81 | if err := step.Check(stateBeforeApplication); err != nil { | ||
82 | return state, fmt.Errorf("Check failed: %s", err) | ||
83 | } | ||
84 | } else { | ||
85 | if err := step.Check(state); err != nil { | ||
86 | return state, fmt.Errorf("Check failed: %s", err) | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | |||
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. | ||
94 | var p *terraform.Plan | ||
95 | if p, err = ctx.Plan(); err != nil { | ||
96 | return state, fmt.Errorf("Error on follow-up plan: %s", err) | ||
97 | } | ||
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) | ||
101 | } else { | ||
102 | return state, fmt.Errorf( | ||
103 | "After applying this step, the plan was not empty:\n\n%s", p) | ||
104 | } | ||
105 | } | ||
106 | |||
107 | // And another after a Refresh. | ||
108 | if !step.Destroy || (step.Destroy && !step.PreventPostDestroyRefresh) { | ||
109 | state, err = ctx.Refresh() | ||
110 | if err != nil { | ||
111 | return state, fmt.Errorf( | ||
112 | "Error on follow-up refresh: %s", err) | ||
113 | } | ||
114 | } | ||
115 | if p, err = ctx.Plan(); err != nil { | ||
116 | return state, fmt.Errorf("Error on second follow-up plan: %s", err) | ||
117 | } | ||
118 | empty := p.Diff == nil || p.Diff.Empty() | ||
119 | |||
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. | ||
126 | if step.Destroy { | ||
127 | empty = true | ||
128 | |||
129 | for _, moduleDiff := range p.Diff.Modules { | ||
130 | for k, instanceDiff := range moduleDiff.Resources { | ||
131 | if !strings.HasPrefix(k, "data.") { | ||
132 | empty = false | ||
133 | break | ||
134 | } | ||
135 | |||
136 | if !instanceDiff.Destroy { | ||
137 | empty = false | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | |||
143 | if !empty { | ||
144 | if step.ExpectNonEmptyPlan { | ||
145 | log.Printf("[INFO] Got non-empty plan, as expected:\n\n%s", p) | ||
146 | } else { | ||
147 | return state, fmt.Errorf( | ||
148 | "After applying this step and refreshing, "+ | ||
149 | "the plan was not empty:\n\n%s", p) | ||
150 | } | ||
151 | } | ||
152 | |||
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!") | ||
156 | } | ||
157 | |||
158 | // Made it here? Good job test step! | ||
159 | return state, nil | ||
160 | } | ||