diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/shadow_context.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/shadow_context.go | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_context.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_context.go new file mode 100644 index 0000000..5588af2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/shadow_context.go | |||
@@ -0,0 +1,158 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strings" | ||
6 | |||
7 | "github.com/hashicorp/go-multierror" | ||
8 | "github.com/mitchellh/copystructure" | ||
9 | ) | ||
10 | |||
11 | // newShadowContext creates a new context that will shadow the given context | ||
12 | // when walking the graph. The resulting context should be used _only once_ | ||
13 | // for a graph walk. | ||
14 | // | ||
15 | // The returned Shadow should be closed after the graph walk with the | ||
16 | // real context is complete. Errors from the shadow can be retrieved there. | ||
17 | // | ||
18 | // Most importantly, any operations done on the shadow context (the returned | ||
19 | // context) will NEVER affect the real context. All structures are deep | ||
20 | // copied, no real providers or resources are used, etc. | ||
21 | func newShadowContext(c *Context) (*Context, *Context, Shadow) { | ||
22 | // Copy the targets | ||
23 | targetRaw, err := copystructure.Copy(c.targets) | ||
24 | if err != nil { | ||
25 | panic(err) | ||
26 | } | ||
27 | |||
28 | // Copy the variables | ||
29 | varRaw, err := copystructure.Copy(c.variables) | ||
30 | if err != nil { | ||
31 | panic(err) | ||
32 | } | ||
33 | |||
34 | // Copy the provider inputs | ||
35 | providerInputRaw, err := copystructure.Copy(c.providerInputConfig) | ||
36 | if err != nil { | ||
37 | panic(err) | ||
38 | } | ||
39 | |||
40 | // The factories | ||
41 | componentsReal, componentsShadow := newShadowComponentFactory(c.components) | ||
42 | |||
43 | // Create the shadow | ||
44 | shadow := &Context{ | ||
45 | components: componentsShadow, | ||
46 | destroy: c.destroy, | ||
47 | diff: c.diff.DeepCopy(), | ||
48 | hooks: nil, | ||
49 | meta: c.meta, | ||
50 | module: c.module, | ||
51 | state: c.state.DeepCopy(), | ||
52 | targets: targetRaw.([]string), | ||
53 | variables: varRaw.(map[string]interface{}), | ||
54 | |||
55 | // NOTE(mitchellh): This is not going to work for shadows that are | ||
56 | // testing that input results in the proper end state. At the time | ||
57 | // of writing, input is not used in any state-changing graph | ||
58 | // walks anyways, so this checks nothing. We set it to this to avoid | ||
59 | // any panics but even a "nil" value worked here. | ||
60 | uiInput: new(MockUIInput), | ||
61 | |||
62 | // Hardcoded to 4 since parallelism in the shadow doesn't matter | ||
63 | // a ton since we're doing far less compared to the real side | ||
64 | // and our operations are MUCH faster. | ||
65 | parallelSem: NewSemaphore(4), | ||
66 | providerInputConfig: providerInputRaw.(map[string]map[string]interface{}), | ||
67 | } | ||
68 | |||
69 | // Create the real context. This is effectively just a copy of | ||
70 | // the context given except we need to modify some of the values | ||
71 | // to point to the real side of a shadow so the shadow can compare values. | ||
72 | real := &Context{ | ||
73 | // The fields below are changed. | ||
74 | components: componentsReal, | ||
75 | |||
76 | // The fields below are direct copies | ||
77 | destroy: c.destroy, | ||
78 | diff: c.diff, | ||
79 | // diffLock - no copy | ||
80 | hooks: c.hooks, | ||
81 | meta: c.meta, | ||
82 | module: c.module, | ||
83 | sh: c.sh, | ||
84 | state: c.state, | ||
85 | // stateLock - no copy | ||
86 | targets: c.targets, | ||
87 | uiInput: c.uiInput, | ||
88 | variables: c.variables, | ||
89 | |||
90 | // l - no copy | ||
91 | parallelSem: c.parallelSem, | ||
92 | providerInputConfig: c.providerInputConfig, | ||
93 | runContext: c.runContext, | ||
94 | runContextCancel: c.runContextCancel, | ||
95 | shadowErr: c.shadowErr, | ||
96 | } | ||
97 | |||
98 | return real, shadow, &shadowContextCloser{ | ||
99 | Components: componentsShadow, | ||
100 | } | ||
101 | } | ||
102 | |||
103 | // shadowContextVerify takes the real and shadow context and verifies they | ||
104 | // have equal diffs and states. | ||
105 | func shadowContextVerify(real, shadow *Context) error { | ||
106 | var result error | ||
107 | |||
108 | // The states compared must be pruned so they're minimal/clean | ||
109 | real.state.prune() | ||
110 | shadow.state.prune() | ||
111 | |||
112 | // Compare the states | ||
113 | if !real.state.Equal(shadow.state) { | ||
114 | result = multierror.Append(result, fmt.Errorf( | ||
115 | "Real and shadow states do not match! "+ | ||
116 | "Real state:\n\n%s\n\n"+ | ||
117 | "Shadow state:\n\n%s\n\n", | ||
118 | real.state, shadow.state)) | ||
119 | } | ||
120 | |||
121 | // Compare the diffs | ||
122 | if !real.diff.Equal(shadow.diff) { | ||
123 | result = multierror.Append(result, fmt.Errorf( | ||
124 | "Real and shadow diffs do not match! "+ | ||
125 | "Real diff:\n\n%s\n\n"+ | ||
126 | "Shadow diff:\n\n%s\n\n", | ||
127 | real.diff, shadow.diff)) | ||
128 | } | ||
129 | |||
130 | return result | ||
131 | } | ||
132 | |||
133 | // shadowContextCloser is the io.Closer returned by newShadowContext that | ||
134 | // closes all the shadows and returns the results. | ||
135 | type shadowContextCloser struct { | ||
136 | Components *shadowComponentFactory | ||
137 | } | ||
138 | |||
139 | // Close closes the shadow context. | ||
140 | func (c *shadowContextCloser) CloseShadow() error { | ||
141 | return c.Components.CloseShadow() | ||
142 | } | ||
143 | |||
144 | func (c *shadowContextCloser) ShadowError() error { | ||
145 | err := c.Components.ShadowError() | ||
146 | if err == nil { | ||
147 | return nil | ||
148 | } | ||
149 | |||
150 | // This is a sad edge case: if the configuration contains uuid() at | ||
151 | // any point, we cannot reason aboyt the shadow execution. Tested | ||
152 | // with Context2Plan_shadowUuid. | ||
153 | if strings.Contains(err.Error(), "uuid()") { | ||
154 | err = nil | ||
155 | } | ||
156 | |||
157 | return err | ||
158 | } | ||