diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/debug.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/debug.go | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/debug.go b/vendor/github.com/hashicorp/terraform/terraform/debug.go new file mode 100644 index 0000000..265339f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/debug.go | |||
@@ -0,0 +1,523 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "archive/tar" | ||
5 | "bytes" | ||
6 | "compress/gzip" | ||
7 | "encoding/json" | ||
8 | "fmt" | ||
9 | "io" | ||
10 | "os" | ||
11 | "path/filepath" | ||
12 | "sync" | ||
13 | "time" | ||
14 | ) | ||
15 | |||
16 | // DebugInfo is the global handler for writing the debug archive. All methods | ||
17 | // are safe to call concurrently. Setting DebugInfo to nil will disable writing | ||
18 | // the debug archive. All methods are safe to call on the nil value. | ||
19 | var dbug *debugInfo | ||
20 | |||
21 | // SetDebugInfo initializes the debug handler with a backing file in the | ||
22 | // provided directory. This must be called before any other terraform package | ||
23 | // operations or not at all. Once his is called, CloseDebugInfo should be | ||
24 | // called before program exit. | ||
25 | func SetDebugInfo(path string) error { | ||
26 | if os.Getenv("TF_DEBUG") == "" { | ||
27 | return nil | ||
28 | } | ||
29 | |||
30 | di, err := newDebugInfoFile(path) | ||
31 | if err != nil { | ||
32 | return err | ||
33 | } | ||
34 | |||
35 | dbug = di | ||
36 | return nil | ||
37 | } | ||
38 | |||
39 | // CloseDebugInfo is the exported interface to Close the debug info handler. | ||
40 | // The debug handler needs to be closed before program exit, so we export this | ||
41 | // function to be deferred in the appropriate entrypoint for our executable. | ||
42 | func CloseDebugInfo() error { | ||
43 | return dbug.Close() | ||
44 | } | ||
45 | |||
46 | // newDebugInfoFile initializes the global debug handler with a backing file in | ||
47 | // the provided directory. | ||
48 | func newDebugInfoFile(dir string) (*debugInfo, error) { | ||
49 | err := os.MkdirAll(dir, 0755) | ||
50 | if err != nil { | ||
51 | return nil, err | ||
52 | } | ||
53 | |||
54 | // FIXME: not guaranteed unique, but good enough for now | ||
55 | name := fmt.Sprintf("debug-%s", time.Now().Format("2006-01-02-15-04-05.999999999")) | ||
56 | archivePath := filepath.Join(dir, name+".tar.gz") | ||
57 | |||
58 | f, err := os.OpenFile(archivePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) | ||
59 | if err != nil { | ||
60 | return nil, err | ||
61 | } | ||
62 | return newDebugInfo(name, f) | ||
63 | } | ||
64 | |||
65 | // newDebugInfo initializes the global debug handler. | ||
66 | func newDebugInfo(name string, w io.Writer) (*debugInfo, error) { | ||
67 | gz := gzip.NewWriter(w) | ||
68 | |||
69 | d := &debugInfo{ | ||
70 | name: name, | ||
71 | w: w, | ||
72 | gz: gz, | ||
73 | tar: tar.NewWriter(gz), | ||
74 | } | ||
75 | |||
76 | // create the subdirs we need | ||
77 | topHdr := &tar.Header{ | ||
78 | Name: name, | ||
79 | Typeflag: tar.TypeDir, | ||
80 | Mode: 0755, | ||
81 | } | ||
82 | graphsHdr := &tar.Header{ | ||
83 | Name: name + "/graphs", | ||
84 | Typeflag: tar.TypeDir, | ||
85 | Mode: 0755, | ||
86 | } | ||
87 | err := d.tar.WriteHeader(topHdr) | ||
88 | // if the first errors, the second will too | ||
89 | err = d.tar.WriteHeader(graphsHdr) | ||
90 | if err != nil { | ||
91 | return nil, err | ||
92 | } | ||
93 | |||
94 | return d, nil | ||
95 | } | ||
96 | |||
97 | // debugInfo provides various methods for writing debug information to a | ||
98 | // central archive. The debugInfo struct should be initialized once before any | ||
99 | // output is written, and Close should be called before program exit. All | ||
100 | // exported methods on debugInfo will be safe for concurrent use. The exported | ||
101 | // methods are also all safe to call on a nil pointer, so that there is no need | ||
102 | // for conditional blocks before writing debug information. | ||
103 | // | ||
104 | // Each write operation done by the debugInfo will flush the gzip.Writer and | ||
105 | // tar.Writer, and call Sync() or Flush() on the output writer as needed. This | ||
106 | // ensures that as much data as possible is written to storage in the event of | ||
107 | // a crash. The append format of the tar file, and the stream format of the | ||
108 | // gzip writer allow easy recovery f the data in the event that the debugInfo | ||
109 | // is not closed before program exit. | ||
110 | type debugInfo struct { | ||
111 | sync.Mutex | ||
112 | |||
113 | // archive root directory name | ||
114 | name string | ||
115 | |||
116 | // current operation phase | ||
117 | phase string | ||
118 | |||
119 | // step is monotonic counter for for recording the order of operations | ||
120 | step int | ||
121 | |||
122 | // flag to protect Close() | ||
123 | closed bool | ||
124 | |||
125 | // the debug log output is in a tar.gz format, written to the io.Writer w | ||
126 | w io.Writer | ||
127 | gz *gzip.Writer | ||
128 | tar *tar.Writer | ||
129 | } | ||
130 | |||
131 | // Set the name of the current operational phase in the debug handler. Each file | ||
132 | // in the archive will contain the name of the phase in which it was created, | ||
133 | // i.e. "input", "apply", "plan", "refresh", "validate" | ||
134 | func (d *debugInfo) SetPhase(phase string) { | ||
135 | if d == nil { | ||
136 | return | ||
137 | } | ||
138 | d.Lock() | ||
139 | defer d.Unlock() | ||
140 | |||
141 | d.phase = phase | ||
142 | } | ||
143 | |||
144 | // Close the debugInfo, finalizing the data in storage. This closes the | ||
145 | // tar.Writer, the gzip.Wrtier, and if the output writer is an io.Closer, it is | ||
146 | // also closed. | ||
147 | func (d *debugInfo) Close() error { | ||
148 | if d == nil { | ||
149 | return nil | ||
150 | } | ||
151 | |||
152 | d.Lock() | ||
153 | defer d.Unlock() | ||
154 | |||
155 | if d.closed { | ||
156 | return nil | ||
157 | } | ||
158 | d.closed = true | ||
159 | |||
160 | d.tar.Close() | ||
161 | d.gz.Close() | ||
162 | |||
163 | if c, ok := d.w.(io.Closer); ok { | ||
164 | return c.Close() | ||
165 | } | ||
166 | return nil | ||
167 | } | ||
168 | |||
169 | // debug buffer is an io.WriteCloser that will write itself to the debug | ||
170 | // archive when closed. | ||
171 | type debugBuffer struct { | ||
172 | debugInfo *debugInfo | ||
173 | name string | ||
174 | buf bytes.Buffer | ||
175 | } | ||
176 | |||
177 | func (b *debugBuffer) Write(d []byte) (int, error) { | ||
178 | return b.buf.Write(d) | ||
179 | } | ||
180 | |||
181 | func (b *debugBuffer) Close() error { | ||
182 | return b.debugInfo.WriteFile(b.name, b.buf.Bytes()) | ||
183 | } | ||
184 | |||
185 | // ioutils only has a noop ReadCloser | ||
186 | type nopWriteCloser struct{} | ||
187 | |||
188 | func (nopWriteCloser) Write([]byte) (int, error) { return 0, nil } | ||
189 | func (nopWriteCloser) Close() error { return nil } | ||
190 | |||
191 | // NewFileWriter returns an io.WriteClose that will be buffered and written to | ||
192 | // the debug archive when closed. | ||
193 | func (d *debugInfo) NewFileWriter(name string) io.WriteCloser { | ||
194 | if d == nil { | ||
195 | return nopWriteCloser{} | ||
196 | } | ||
197 | |||
198 | return &debugBuffer{ | ||
199 | debugInfo: d, | ||
200 | name: name, | ||
201 | } | ||
202 | } | ||
203 | |||
204 | type syncer interface { | ||
205 | Sync() error | ||
206 | } | ||
207 | |||
208 | type flusher interface { | ||
209 | Flush() error | ||
210 | } | ||
211 | |||
212 | // Flush the tar.Writer and the gzip.Writer. Flush() or Sync() will be called | ||
213 | // on the output writer if they are available. | ||
214 | func (d *debugInfo) flush() { | ||
215 | d.tar.Flush() | ||
216 | d.gz.Flush() | ||
217 | |||
218 | if f, ok := d.w.(flusher); ok { | ||
219 | f.Flush() | ||
220 | } | ||
221 | |||
222 | if s, ok := d.w.(syncer); ok { | ||
223 | s.Sync() | ||
224 | } | ||
225 | } | ||
226 | |||
227 | // WriteFile writes data as a single file to the debug arhive. | ||
228 | func (d *debugInfo) WriteFile(name string, data []byte) error { | ||
229 | if d == nil { | ||
230 | return nil | ||
231 | } | ||
232 | |||
233 | d.Lock() | ||
234 | defer d.Unlock() | ||
235 | return d.writeFile(name, data) | ||
236 | } | ||
237 | |||
238 | func (d *debugInfo) writeFile(name string, data []byte) error { | ||
239 | defer d.flush() | ||
240 | path := fmt.Sprintf("%s/%d-%s-%s", d.name, d.step, d.phase, name) | ||
241 | d.step++ | ||
242 | |||
243 | hdr := &tar.Header{ | ||
244 | Name: path, | ||
245 | Mode: 0644, | ||
246 | Size: int64(len(data)), | ||
247 | } | ||
248 | err := d.tar.WriteHeader(hdr) | ||
249 | if err != nil { | ||
250 | return err | ||
251 | } | ||
252 | |||
253 | _, err = d.tar.Write(data) | ||
254 | return err | ||
255 | } | ||
256 | |||
257 | // DebugHook implements all methods of the terraform.Hook interface, and writes | ||
258 | // the arguments to a file in the archive. When a suitable format for the | ||
259 | // argument isn't available, the argument is encoded using json.Marshal. If the | ||
260 | // debug handler is nil, all DebugHook methods are noop, so no time is spent in | ||
261 | // marshaling the data structures. | ||
262 | type DebugHook struct{} | ||
263 | |||
264 | func (*DebugHook) PreApply(ii *InstanceInfo, is *InstanceState, id *InstanceDiff) (HookAction, error) { | ||
265 | if dbug == nil { | ||
266 | return HookActionContinue, nil | ||
267 | } | ||
268 | |||
269 | var buf bytes.Buffer | ||
270 | |||
271 | if ii != nil { | ||
272 | buf.WriteString(ii.HumanId() + "\n") | ||
273 | } | ||
274 | |||
275 | if is != nil { | ||
276 | buf.WriteString(is.String() + "\n") | ||
277 | } | ||
278 | |||
279 | idCopy, err := id.Copy() | ||
280 | if err != nil { | ||
281 | return HookActionContinue, err | ||
282 | } | ||
283 | js, err := json.MarshalIndent(idCopy, "", " ") | ||
284 | if err != nil { | ||
285 | return HookActionContinue, err | ||
286 | } | ||
287 | buf.Write(js) | ||
288 | |||
289 | dbug.WriteFile("hook-PreApply", buf.Bytes()) | ||
290 | |||
291 | return HookActionContinue, nil | ||
292 | } | ||
293 | |||
294 | func (*DebugHook) PostApply(ii *InstanceInfo, is *InstanceState, err error) (HookAction, error) { | ||
295 | if dbug == nil { | ||
296 | return HookActionContinue, nil | ||
297 | } | ||
298 | |||
299 | var buf bytes.Buffer | ||
300 | |||
301 | if ii != nil { | ||
302 | buf.WriteString(ii.HumanId() + "\n") | ||
303 | } | ||
304 | |||
305 | if is != nil { | ||
306 | buf.WriteString(is.String() + "\n") | ||
307 | } | ||
308 | |||
309 | if err != nil { | ||
310 | buf.WriteString(err.Error()) | ||
311 | } | ||
312 | |||
313 | dbug.WriteFile("hook-PostApply", buf.Bytes()) | ||
314 | |||
315 | return HookActionContinue, nil | ||
316 | } | ||
317 | |||
318 | func (*DebugHook) PreDiff(ii *InstanceInfo, is *InstanceState) (HookAction, error) { | ||
319 | if dbug == nil { | ||
320 | return HookActionContinue, nil | ||
321 | } | ||
322 | |||
323 | var buf bytes.Buffer | ||
324 | if ii != nil { | ||
325 | buf.WriteString(ii.HumanId() + "\n") | ||
326 | } | ||
327 | |||
328 | if is != nil { | ||
329 | buf.WriteString(is.String()) | ||
330 | buf.WriteString("\n") | ||
331 | } | ||
332 | dbug.WriteFile("hook-PreDiff", buf.Bytes()) | ||
333 | |||
334 | return HookActionContinue, nil | ||
335 | } | ||
336 | |||
337 | func (*DebugHook) PostDiff(ii *InstanceInfo, id *InstanceDiff) (HookAction, error) { | ||
338 | if dbug == nil { | ||
339 | return HookActionContinue, nil | ||
340 | } | ||
341 | |||
342 | var buf bytes.Buffer | ||
343 | if ii != nil { | ||
344 | buf.WriteString(ii.HumanId() + "\n") | ||
345 | } | ||
346 | |||
347 | idCopy, err := id.Copy() | ||
348 | if err != nil { | ||
349 | return HookActionContinue, err | ||
350 | } | ||
351 | js, err := json.MarshalIndent(idCopy, "", " ") | ||
352 | if err != nil { | ||
353 | return HookActionContinue, err | ||
354 | } | ||
355 | buf.Write(js) | ||
356 | |||
357 | dbug.WriteFile("hook-PostDiff", buf.Bytes()) | ||
358 | |||
359 | return HookActionContinue, nil | ||
360 | } | ||
361 | |||
362 | func (*DebugHook) PreProvisionResource(ii *InstanceInfo, is *InstanceState) (HookAction, error) { | ||
363 | if dbug == nil { | ||
364 | return HookActionContinue, nil | ||
365 | } | ||
366 | |||
367 | var buf bytes.Buffer | ||
368 | if ii != nil { | ||
369 | buf.WriteString(ii.HumanId() + "\n") | ||
370 | } | ||
371 | |||
372 | if is != nil { | ||
373 | buf.WriteString(is.String()) | ||
374 | buf.WriteString("\n") | ||
375 | } | ||
376 | dbug.WriteFile("hook-PreProvisionResource", buf.Bytes()) | ||
377 | |||
378 | return HookActionContinue, nil | ||
379 | } | ||
380 | |||
381 | func (*DebugHook) PostProvisionResource(ii *InstanceInfo, is *InstanceState) (HookAction, error) { | ||
382 | if dbug == nil { | ||
383 | return HookActionContinue, nil | ||
384 | } | ||
385 | |||
386 | var buf bytes.Buffer | ||
387 | if ii != nil { | ||
388 | buf.WriteString(ii.HumanId()) | ||
389 | buf.WriteString("\n") | ||
390 | } | ||
391 | |||
392 | if is != nil { | ||
393 | buf.WriteString(is.String()) | ||
394 | buf.WriteString("\n") | ||
395 | } | ||
396 | dbug.WriteFile("hook-PostProvisionResource", buf.Bytes()) | ||
397 | return HookActionContinue, nil | ||
398 | } | ||
399 | |||
400 | func (*DebugHook) PreProvision(ii *InstanceInfo, s string) (HookAction, error) { | ||
401 | if dbug == nil { | ||
402 | return HookActionContinue, nil | ||
403 | } | ||
404 | |||
405 | var buf bytes.Buffer | ||
406 | if ii != nil { | ||
407 | buf.WriteString(ii.HumanId()) | ||
408 | buf.WriteString("\n") | ||
409 | } | ||
410 | buf.WriteString(s + "\n") | ||
411 | |||
412 | dbug.WriteFile("hook-PreProvision", buf.Bytes()) | ||
413 | return HookActionContinue, nil | ||
414 | } | ||
415 | |||
416 | func (*DebugHook) PostProvision(ii *InstanceInfo, s string, err error) (HookAction, error) { | ||
417 | if dbug == nil { | ||
418 | return HookActionContinue, nil | ||
419 | } | ||
420 | |||
421 | var buf bytes.Buffer | ||
422 | if ii != nil { | ||
423 | buf.WriteString(ii.HumanId() + "\n") | ||
424 | } | ||
425 | buf.WriteString(s + "\n") | ||
426 | |||
427 | dbug.WriteFile("hook-PostProvision", buf.Bytes()) | ||
428 | return HookActionContinue, nil | ||
429 | } | ||
430 | |||
431 | func (*DebugHook) ProvisionOutput(ii *InstanceInfo, s1 string, s2 string) { | ||
432 | if dbug == nil { | ||
433 | return | ||
434 | } | ||
435 | |||
436 | var buf bytes.Buffer | ||
437 | if ii != nil { | ||
438 | buf.WriteString(ii.HumanId()) | ||
439 | buf.WriteString("\n") | ||
440 | } | ||
441 | buf.WriteString(s1 + "\n") | ||
442 | buf.WriteString(s2 + "\n") | ||
443 | |||
444 | dbug.WriteFile("hook-ProvisionOutput", buf.Bytes()) | ||
445 | } | ||
446 | |||
447 | func (*DebugHook) PreRefresh(ii *InstanceInfo, is *InstanceState) (HookAction, error) { | ||
448 | if dbug == nil { | ||
449 | return HookActionContinue, nil | ||
450 | } | ||
451 | |||
452 | var buf bytes.Buffer | ||
453 | if ii != nil { | ||
454 | buf.WriteString(ii.HumanId() + "\n") | ||
455 | } | ||
456 | |||
457 | if is != nil { | ||
458 | buf.WriteString(is.String()) | ||
459 | buf.WriteString("\n") | ||
460 | } | ||
461 | dbug.WriteFile("hook-PreRefresh", buf.Bytes()) | ||
462 | return HookActionContinue, nil | ||
463 | } | ||
464 | |||
465 | func (*DebugHook) PostRefresh(ii *InstanceInfo, is *InstanceState) (HookAction, error) { | ||
466 | if dbug == nil { | ||
467 | return HookActionContinue, nil | ||
468 | } | ||
469 | |||
470 | var buf bytes.Buffer | ||
471 | if ii != nil { | ||
472 | buf.WriteString(ii.HumanId()) | ||
473 | buf.WriteString("\n") | ||
474 | } | ||
475 | |||
476 | if is != nil { | ||
477 | buf.WriteString(is.String()) | ||
478 | buf.WriteString("\n") | ||
479 | } | ||
480 | dbug.WriteFile("hook-PostRefresh", buf.Bytes()) | ||
481 | return HookActionContinue, nil | ||
482 | } | ||
483 | |||
484 | func (*DebugHook) PreImportState(ii *InstanceInfo, s string) (HookAction, error) { | ||
485 | if dbug == nil { | ||
486 | return HookActionContinue, nil | ||
487 | } | ||
488 | |||
489 | var buf bytes.Buffer | ||
490 | if ii != nil { | ||
491 | buf.WriteString(ii.HumanId()) | ||
492 | buf.WriteString("\n") | ||
493 | } | ||
494 | buf.WriteString(s + "\n") | ||
495 | |||
496 | dbug.WriteFile("hook-PreImportState", buf.Bytes()) | ||
497 | return HookActionContinue, nil | ||
498 | } | ||
499 | |||
500 | func (*DebugHook) PostImportState(ii *InstanceInfo, iss []*InstanceState) (HookAction, error) { | ||
501 | if dbug == nil { | ||
502 | return HookActionContinue, nil | ||
503 | } | ||
504 | |||
505 | var buf bytes.Buffer | ||
506 | |||
507 | if ii != nil { | ||
508 | buf.WriteString(ii.HumanId() + "\n") | ||
509 | } | ||
510 | |||
511 | for _, is := range iss { | ||
512 | if is != nil { | ||
513 | buf.WriteString(is.String() + "\n") | ||
514 | } | ||
515 | } | ||
516 | dbug.WriteFile("hook-PostImportState", buf.Bytes()) | ||
517 | return HookActionContinue, nil | ||
518 | } | ||
519 | |||
520 | // skip logging this for now, since it could be huge | ||
521 | func (*DebugHook) PostStateUpdate(*State) (HookAction, error) { | ||
522 | return HookActionContinue, nil | ||
523 | } | ||