diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/command/format/plan.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/command/format/plan.go | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/command/format/plan.go b/vendor/github.com/hashicorp/terraform/command/format/plan.go new file mode 100644 index 0000000..098653f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/format/plan.go | |||
@@ -0,0 +1,302 @@ | |||
1 | package format | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "fmt" | ||
6 | "log" | ||
7 | "sort" | ||
8 | "strings" | ||
9 | |||
10 | "github.com/mitchellh/colorstring" | ||
11 | |||
12 | "github.com/hashicorp/terraform/addrs" | ||
13 | "github.com/hashicorp/terraform/plans" | ||
14 | "github.com/hashicorp/terraform/states" | ||
15 | "github.com/hashicorp/terraform/terraform" | ||
16 | ) | ||
17 | |||
18 | // Plan is a representation of a plan optimized for display to | ||
19 | // an end-user, as opposed to terraform.Plan which is for internal use. | ||
20 | // | ||
21 | // DisplayPlan excludes implementation details that may otherwise appear | ||
22 | // in the main plan, such as destroy actions on data sources (which are | ||
23 | // there only to clean up the state). | ||
24 | type Plan struct { | ||
25 | Resources []*InstanceDiff | ||
26 | } | ||
27 | |||
28 | // InstanceDiff is a representation of an instance diff optimized | ||
29 | // for display, in conjunction with DisplayPlan. | ||
30 | type InstanceDiff struct { | ||
31 | Addr *terraform.ResourceAddress | ||
32 | Action plans.Action | ||
33 | |||
34 | // Attributes describes changes to the attributes of the instance. | ||
35 | // | ||
36 | // For destroy diffs this is always nil. | ||
37 | Attributes []*AttributeDiff | ||
38 | |||
39 | Tainted bool | ||
40 | Deposed bool | ||
41 | } | ||
42 | |||
43 | // AttributeDiff is a representation of an attribute diff optimized | ||
44 | // for display, in conjunction with DisplayInstanceDiff. | ||
45 | type AttributeDiff struct { | ||
46 | // Path is a dot-delimited traversal through possibly many levels of list and map structure, | ||
47 | // intended for display purposes only. | ||
48 | Path string | ||
49 | |||
50 | Action plans.Action | ||
51 | |||
52 | OldValue string | ||
53 | NewValue string | ||
54 | |||
55 | NewComputed bool | ||
56 | Sensitive bool | ||
57 | ForcesNew bool | ||
58 | } | ||
59 | |||
60 | // PlanStats gives summary counts for a Plan. | ||
61 | type PlanStats struct { | ||
62 | ToAdd, ToChange, ToDestroy int | ||
63 | } | ||
64 | |||
65 | // NewPlan produces a display-oriented Plan from a terraform.Plan. | ||
66 | func NewPlan(changes *plans.Changes) *Plan { | ||
67 | log.Printf("[TRACE] NewPlan for %#v", changes) | ||
68 | ret := &Plan{} | ||
69 | if changes == nil { | ||
70 | // Nothing to do! | ||
71 | return ret | ||
72 | } | ||
73 | |||
74 | for _, rc := range changes.Resources { | ||
75 | addr := rc.Addr | ||
76 | log.Printf("[TRACE] NewPlan found %s (%s)", addr, rc.Action) | ||
77 | dataSource := addr.Resource.Resource.Mode == addrs.DataResourceMode | ||
78 | |||
79 | // We create "delete" actions for data resources so we can clean | ||
80 | // up their entries in state, but this is an implementation detail | ||
81 | // that users shouldn't see. | ||
82 | if dataSource && rc.Action == plans.Delete { | ||
83 | continue | ||
84 | } | ||
85 | |||
86 | // For now we'll shim this to work with our old types. | ||
87 | // TODO: Update for the new plan types, ideally also switching over to | ||
88 | // a structural diff renderer instead of a flat renderer. | ||
89 | did := &InstanceDiff{ | ||
90 | Addr: terraform.NewLegacyResourceInstanceAddress(addr), | ||
91 | Action: rc.Action, | ||
92 | } | ||
93 | |||
94 | if rc.DeposedKey != states.NotDeposed { | ||
95 | did.Deposed = true | ||
96 | } | ||
97 | |||
98 | // Since this is just a temporary stub implementation on the way | ||
99 | // to us replacing this with the structural diff renderer, we currently | ||
100 | // don't include any attributes here. | ||
101 | // FIXME: Implement the structural diff renderer to replace this | ||
102 | // codepath altogether. | ||
103 | |||
104 | ret.Resources = append(ret.Resources, did) | ||
105 | } | ||
106 | |||
107 | // Sort the instance diffs by their addresses for display. | ||
108 | sort.Slice(ret.Resources, func(i, j int) bool { | ||
109 | iAddr := ret.Resources[i].Addr | ||
110 | jAddr := ret.Resources[j].Addr | ||
111 | return iAddr.Less(jAddr) | ||
112 | }) | ||
113 | |||
114 | return ret | ||
115 | } | ||
116 | |||
117 | // Format produces and returns a text representation of the receiving plan | ||
118 | // intended for display in a terminal. | ||
119 | // | ||
120 | // If color is not nil, it is used to colorize the output. | ||
121 | func (p *Plan) Format(color *colorstring.Colorize) string { | ||
122 | if p.Empty() { | ||
123 | return "This plan does nothing." | ||
124 | } | ||
125 | |||
126 | if color == nil { | ||
127 | color = &colorstring.Colorize{ | ||
128 | Colors: colorstring.DefaultColors, | ||
129 | Reset: false, | ||
130 | } | ||
131 | } | ||
132 | |||
133 | // Find the longest path length of all the paths that are changing, | ||
134 | // so we can align them all. | ||
135 | keyLen := 0 | ||
136 | for _, r := range p.Resources { | ||
137 | for _, attr := range r.Attributes { | ||
138 | key := attr.Path | ||
139 | |||
140 | if len(key) > keyLen { | ||
141 | keyLen = len(key) | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | buf := new(bytes.Buffer) | ||
147 | for _, r := range p.Resources { | ||
148 | formatPlanInstanceDiff(buf, r, keyLen, color) | ||
149 | } | ||
150 | |||
151 | return strings.TrimSpace(buf.String()) | ||
152 | } | ||
153 | |||
154 | // Stats returns statistics about the plan | ||
155 | func (p *Plan) Stats() PlanStats { | ||
156 | var ret PlanStats | ||
157 | for _, r := range p.Resources { | ||
158 | switch r.Action { | ||
159 | case plans.Create: | ||
160 | ret.ToAdd++ | ||
161 | case plans.Update: | ||
162 | ret.ToChange++ | ||
163 | case plans.DeleteThenCreate, plans.CreateThenDelete: | ||
164 | ret.ToAdd++ | ||
165 | ret.ToDestroy++ | ||
166 | case plans.Delete: | ||
167 | ret.ToDestroy++ | ||
168 | } | ||
169 | } | ||
170 | return ret | ||
171 | } | ||
172 | |||
173 | // ActionCounts returns the number of diffs for each action type | ||
174 | func (p *Plan) ActionCounts() map[plans.Action]int { | ||
175 | ret := map[plans.Action]int{} | ||
176 | for _, r := range p.Resources { | ||
177 | ret[r.Action]++ | ||
178 | } | ||
179 | return ret | ||
180 | } | ||
181 | |||
182 | // Empty returns true if there is at least one resource diff in the receiving plan. | ||
183 | func (p *Plan) Empty() bool { | ||
184 | return len(p.Resources) == 0 | ||
185 | } | ||
186 | |||
187 | // DiffActionSymbol returns a string that, once passed through a | ||
188 | // colorstring.Colorize, will produce a result that can be written | ||
189 | // to a terminal to produce a symbol made of three printable | ||
190 | // characters, possibly interspersed with VT100 color codes. | ||
191 | func DiffActionSymbol(action plans.Action) string { | ||
192 | switch action { | ||
193 | case plans.DeleteThenCreate: | ||
194 | return "[red]-[reset]/[green]+[reset]" | ||
195 | case plans.CreateThenDelete: | ||
196 | return "[green]+[reset]/[red]-[reset]" | ||
197 | case plans.Create: | ||
198 | return " [green]+[reset]" | ||
199 | case plans.Delete: | ||
200 | return " [red]-[reset]" | ||
201 | case plans.Read: | ||
202 | return " [cyan]<=[reset]" | ||
203 | case plans.Update: | ||
204 | return " [yellow]~[reset]" | ||
205 | default: | ||
206 | return " ?" | ||
207 | } | ||
208 | } | ||
209 | |||
210 | // formatPlanInstanceDiff writes the text representation of the given instance diff | ||
211 | // to the given buffer, using the given colorizer. | ||
212 | func formatPlanInstanceDiff(buf *bytes.Buffer, r *InstanceDiff, keyLen int, colorizer *colorstring.Colorize) { | ||
213 | addrStr := r.Addr.String() | ||
214 | |||
215 | // Determine the color for the text (green for adding, yellow | ||
216 | // for change, red for delete), and symbol, and output the | ||
217 | // resource header. | ||
218 | color := "yellow" | ||
219 | symbol := DiffActionSymbol(r.Action) | ||
220 | oldValues := true | ||
221 | switch r.Action { | ||
222 | case plans.DeleteThenCreate, plans.CreateThenDelete: | ||
223 | color = "yellow" | ||
224 | case plans.Create: | ||
225 | color = "green" | ||
226 | oldValues = false | ||
227 | case plans.Delete: | ||
228 | color = "red" | ||
229 | case plans.Read: | ||
230 | color = "cyan" | ||
231 | oldValues = false | ||
232 | } | ||
233 | |||
234 | var extraStr string | ||
235 | if r.Tainted { | ||
236 | extraStr = extraStr + " (tainted)" | ||
237 | } | ||
238 | if r.Deposed { | ||
239 | extraStr = extraStr + " (deposed)" | ||
240 | } | ||
241 | if r.Action.IsReplace() { | ||
242 | extraStr = extraStr + colorizer.Color(" [red][bold](new resource required)") | ||
243 | } | ||
244 | |||
245 | buf.WriteString( | ||
246 | colorizer.Color(fmt.Sprintf( | ||
247 | "[%s]%s [%s]%s%s\n", | ||
248 | color, symbol, color, addrStr, extraStr, | ||
249 | )), | ||
250 | ) | ||
251 | |||
252 | for _, attr := range r.Attributes { | ||
253 | |||
254 | v := attr.NewValue | ||
255 | var dispV string | ||
256 | switch { | ||
257 | case v == "" && attr.NewComputed: | ||
258 | dispV = "<computed>" | ||
259 | case attr.Sensitive: | ||
260 | dispV = "<sensitive>" | ||
261 | default: | ||
262 | dispV = fmt.Sprintf("%q", v) | ||
263 | } | ||
264 | |||
265 | updateMsg := "" | ||
266 | switch { | ||
267 | case attr.ForcesNew && r.Action.IsReplace(): | ||
268 | updateMsg = colorizer.Color(" [red](forces new resource)") | ||
269 | case attr.Sensitive && oldValues: | ||
270 | updateMsg = colorizer.Color(" [yellow](attribute changed)") | ||
271 | } | ||
272 | |||
273 | if oldValues { | ||
274 | u := attr.OldValue | ||
275 | var dispU string | ||
276 | switch { | ||
277 | case attr.Sensitive: | ||
278 | dispU = "<sensitive>" | ||
279 | default: | ||
280 | dispU = fmt.Sprintf("%q", u) | ||
281 | } | ||
282 | buf.WriteString(fmt.Sprintf( | ||
283 | " %s:%s %s => %s%s\n", | ||
284 | attr.Path, | ||
285 | strings.Repeat(" ", keyLen-len(attr.Path)), | ||
286 | dispU, dispV, | ||
287 | updateMsg, | ||
288 | )) | ||
289 | } else { | ||
290 | buf.WriteString(fmt.Sprintf( | ||
291 | " %s:%s %s%s\n", | ||
292 | attr.Path, | ||
293 | strings.Repeat(" ", keyLen-len(attr.Path)), | ||
294 | dispV, | ||
295 | updateMsg, | ||
296 | )) | ||
297 | } | ||
298 | } | ||
299 | |||
300 | // Write the reset color so we don't bleed color into later text | ||
301 | buf.WriteString(colorizer.Color("[reset]\n")) | ||
302 | } | ||