diff --git a/command/hook_ui.go b/command/hook_ui.go index 06f3cce52..60372123d 100644 --- a/command/hook_ui.go +++ b/command/hook_ui.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "fmt" + "log" "sort" "strings" "sync" @@ -50,6 +51,7 @@ func (h *UiHook) PreApply( n *terraform.InstanceInfo, s *terraform.InstanceState, d *terraform.InstanceDiff) (terraform.HookAction, error) { + log.Printf("[DEBUG] PreApply arguments:\n%#v\n%#v\n%#v", n, s, d) h.once.Do(h.init) id := n.HumanId() @@ -129,9 +131,8 @@ func (h *UiHook) PreApply( attrString = "\n " + attrString } - var stateId, stateIdSuffix string + var stateIdSuffix string if s != nil && s.ID != "" { - stateId = s.ID stateIdSuffix = fmt.Sprintf(" (ID: %s)", truncateId(s.ID, maxIdLen)) } @@ -143,12 +144,12 @@ func (h *UiHook) PreApply( attrString))) // Set a timer to show an operation is still happening - time.AfterFunc(periodicUiTimer, func() { h.stillApplying(id, stateId) }) + time.AfterFunc(periodicUiTimer, func() { h.stillApplying(id, s) }) return terraform.HookActionContinue, nil } -func (h *UiHook) stillApplying(id, stateId string) { +func (h *UiHook) stillApplying(id string, s *terraform.InstanceState) { // Grab the operation. We defer the lock here to avoid the "still..." // message showing up after a completion message. h.l.Lock() @@ -173,8 +174,8 @@ func (h *UiHook) stillApplying(id, stateId string) { } var stateIdSuffix string - if stateId != "" { - stateIdSuffix = fmt.Sprintf("ID: %s, ", truncateId(stateId, maxIdLen)) + if s != nil && s.ID != "" { + stateIdSuffix = fmt.Sprintf("ID: %s, ", truncateId(s.ID, maxIdLen)) } h.ui.Output(h.Colorize.Color(fmt.Sprintf( @@ -186,13 +187,15 @@ func (h *UiHook) stillApplying(id, stateId string) { ))) // Reschedule - time.AfterFunc(periodicUiTimer, func() { h.stillApplying(id, stateId) }) + time.AfterFunc(periodicUiTimer, func() { h.stillApplying(id, s) }) } func (h *UiHook) PostApply( n *terraform.InstanceInfo, s *terraform.InstanceState, applyerr error) (terraform.HookAction, error) { + + log.Printf("[DEBUG] PostApply arguments:\n%#v\n%#v\n%#v", n, s, applyerr) id := n.HumanId() h.l.Lock() @@ -221,10 +224,13 @@ func (h *UiHook) PostApply( // Errors are collected and printed in ApplyCommand, no need to duplicate return terraform.HookActionContinue, nil } + log.Printf("[DEBUG] Printing out output: %#v", msg) - h.ui.Output(h.Colorize.Color(fmt.Sprintf( + colorized := h.Colorize.Color(fmt.Sprintf( "[reset][bold]%s: %s%s[reset]", - id, msg, stateIdSuffix))) + id, msg, stateIdSuffix)) + + h.ui.Output(colorized) return terraform.HookActionContinue, nil } diff --git a/command/hook_ui_test.go b/command/hook_ui_test.go index 1c6476efe..3ba7345b7 100644 --- a/command/hook_ui_test.go +++ b/command/hook_ui_test.go @@ -1,10 +1,72 @@ package command import ( + "bytes" "fmt" "testing" + "time" + + "github.com/hashicorp/terraform/terraform" + "github.com/mitchellh/cli" + "github.com/mitchellh/colorstring" ) +func TestUiHookPostApply_emptyState(t *testing.T) { + colorize := &colorstring.Colorize{ + Colors: colorstring.DefaultColors, + Disable: true, + Reset: true, + } + + ir := bytes.NewReader([]byte{}) + errBuf := bytes.NewBuffer([]byte{}) + outBuf := bytes.NewBuffer([]byte{}) + ui := cli.MockUi{ + InputReader: ir, + ErrorWriter: errBuf, + OutputWriter: outBuf, + } + h := &UiHook{ + Colorize: colorize, + Ui: &ui, + } + h.init() + h.resources = map[string]uiResourceState{ + "data.google_compute_zones.available": uiResourceState{ + Op: uiResourceDestroy, + Start: time.Now(), + }, + } + + mock := &terraform.MockInstanceInfo{ + terraform.InstanceInfo{ + Id: "data.google_compute_zones.available", + ModulePath: []string{"root"}, + Type: "google_compute_zones", + }, + } + n := mock.WithUniqueExtra("destroy") + action, err := h.PostApply(n, nil, nil) + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatal("Expected hook to continue, given: %#v", action) + } + + expectedOutput := "" + output := outBuf.String() + if output != expectedOutput { + t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output) + } + + expectedErrOutput := "" + errOutput := errBuf.String() + if errOutput != expectedErrOutput { + t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) + } +} + func TestTruncateId(t *testing.T) { testCases := []struct { Input string diff --git a/terraform/resource.go b/terraform/resource.go index 0acf0beb2..b1f5822f6 100644 --- a/terraform/resource.go +++ b/terraform/resource.go @@ -72,6 +72,19 @@ type InstanceInfo struct { uniqueExtra string } +type MockInstanceInfo struct { + InstanceInfo +} + +func (m *MockInstanceInfo) WithUniqueExtra(extra string) *InstanceInfo { + return &InstanceInfo{ + Id: m.Id, + ModulePath: m.ModulePath, + Type: m.Type, + uniqueExtra: extra, + } +} + // HumanId is a unique Id that is human-friendly and useful for UI elements. func (i *InstanceInfo) HumanId() string { if i == nil {