diff --git a/internal/command/import.go b/internal/command/import.go index 6e0764e60b..87a16cade9 100644 --- a/internal/command/import.go +++ b/internal/command/import.go @@ -193,7 +193,7 @@ func (c *ImportCommand) Run(args []string) int { c.showDiagnostics(diags) return 1 } - opReq.Hooks = []terraform.Hook{c.uiHook()} + opReq.Hooks = []terraform.Hook{c.uiHook(views.TerraformOperationImport)} { var moreDiags tfdiags.Diagnostics opReq.Variables, moreDiags = c.collectVariableValues() diff --git a/internal/command/meta.go b/internal/command/meta.go index 0d69a5d7bb..cd98a22377 100644 --- a/internal/command/meta.go +++ b/internal/command/meta.go @@ -605,8 +605,8 @@ func (m *Meta) process(args []string) []string { } // uiHook returns the UiHook to use with the context. -func (m *Meta) uiHook() *views.UiHook { - return views.NewUiHook(m.View) +func (m *Meta) uiHook(operation views.TerraformOperation) *views.UiHook { + return views.NewUiHook(m.View, operation) } // confirm asks a yes/no confirmation. diff --git a/internal/command/views/apply.go b/internal/command/views/apply.go index a5fb82d24f..e23960b7b3 100644 --- a/internal/command/views/apply.go +++ b/internal/command/views/apply.go @@ -94,7 +94,7 @@ func (v *ApplyHuman) Operation() Operation { func (v *ApplyHuman) Hooks() []terraform.Hook { return []terraform.Hook{ v.countHook, - NewUiHook(v.view), + NewUiHook(v.view, TerraformOperationApply), } } diff --git a/internal/command/views/hook_ui.go b/internal/command/views/hook_ui.go index 491a51946d..7d15dc7a83 100644 --- a/internal/command/views/hook_ui.go +++ b/internal/command/views/hook_ui.go @@ -25,11 +25,12 @@ import ( const defaultPeriodicUiTimer = 10 * time.Second const maxIdLen = 80 -func NewUiHook(view *View) *UiHook { +func NewUiHook(view *View, operation TerraformOperation) *UiHook { return &UiHook{ view: view, periodicUiTimer: defaultPeriodicUiTimer, resources: make(map[string]uiResourceState), + operation: operation, } } @@ -43,6 +44,8 @@ type UiHook struct { resources map[string]uiResourceState resourcesLock sync.Mutex + + operation TerraformOperation } var _ terraform.Hook = (*UiHook)(nil) @@ -71,6 +74,15 @@ const ( uiResourceNoOp ) +type TerraformOperation byte + +const ( + TerraformOperationApply TerraformOperation = iota + TerraformOperationPlan + TerraformOperationImport + TerraformOperationRefresh +) + func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { dispAddr := addr.String() if gen != states.CurrentGen { @@ -282,25 +294,36 @@ func (h *UiHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generatio } func (h *UiHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (terraform.HookAction, error) { - h.println(fmt.Sprintf( - h.view.colorize.Color("[reset][bold]%s: Importing from ID %q..."), - addr, importID, - )) + if h.operation == TerraformOperationImport { + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: Importing from ID %q..."), + addr, importID, + )) + } else { + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: Importing state... [id=%s]"), + addr, importID, + )) + } return terraform.HookActionContinue, nil } func (h *UiHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (terraform.HookAction, error) { - h.println(fmt.Sprintf( - h.view.colorize.Color("[reset][bold][green]%s: Import prepared!"), - addr, - )) - for _, s := range imported { + if h.operation == TerraformOperationImport { + // We only print PostImportState during an Import operation, for a real + // plan operation the maic doesn't happen until an Apply anyway. + h.println(fmt.Sprintf( - h.view.colorize.Color("[reset][green] Prepared %s for import"), - s.TypeName, + h.view.colorize.Color("[reset][bold][green]%s: Import prepared!"), + addr, )) + for _, s := range imported { + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][green] Prepared %s for import"), + s.TypeName, + )) + } } - return terraform.HookActionContinue, nil } diff --git a/internal/command/views/hook_ui_test.go b/internal/command/views/hook_ui_test.go index ac1b5a1d3c..beff710591 100644 --- a/internal/command/views/hook_ui_test.go +++ b/internal/command/views/hook_ui_test.go @@ -26,7 +26,7 @@ import ( func TestUiHookPreApply_create(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationApply) h.resources = map[string]uiResourceState{ "test_instance.foo": { Op: uiResourceCreate, @@ -83,7 +83,7 @@ func TestUiHookPreApply_create(t *testing.T) { func TestUiHookPreApply_periodicTimer(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationApply) h.periodicUiTimer = 1 * time.Second h.resources = map[string]uiResourceState{ "test_instance.foo": { @@ -147,7 +147,7 @@ test_instance.foo: Still modifying... [id=test, 3s elapsed] func TestUiHookPreApply_destroy(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationApply) h.resources = map[string]uiResourceState{ "test_instance.foo": { Op: uiResourceDestroy, @@ -206,7 +206,7 @@ func TestUiHookPostApply_colorInterpolation(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) view.Configure(&arguments.View{NoColor: false}) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationApply) h.resources = map[string]uiResourceState{ "test_instance.foo[\"[red]\"]": { Op: uiResourceCreate, @@ -258,7 +258,7 @@ func TestUiHookPostApply_colorInterpolation(t *testing.T) { func TestUiHookPostApply_emptyState(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationApply) h.resources = map[string]uiResourceState{ "data.google_compute_zones.available": { Op: uiResourceDestroy, @@ -302,7 +302,7 @@ func TestUiHookPostApply_emptyState(t *testing.T) { func TestPreProvisionInstanceStep(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationApply) addr := addrs.Resource{ Mode: addrs.ManagedResourceMode, @@ -395,7 +395,7 @@ test_instance.foo (winrm): bar t.Run(name, func(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationApply) h.ProvisionOutput(addr, tc.provisioner, tc.input) result := done(t) @@ -412,7 +412,7 @@ test_instance.foo (winrm): bar func TestPreRefresh(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationApply) addr := addrs.Resource{ Mode: addrs.ManagedResourceMode, @@ -445,7 +445,7 @@ func TestPreRefresh(t *testing.T) { func TestPreRefresh_noID(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationApply) addr := addrs.Resource{ Mode: addrs.ManagedResourceMode, @@ -473,10 +473,10 @@ func TestPreRefresh_noID(t *testing.T) { } // Test the very simple PreImportState hook. -func TestPreImportState(t *testing.T) { +func TestPreImportState_Import(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationImport) addr := addrs.Resource{ Mode: addrs.ManagedResourceMode, @@ -499,13 +499,39 @@ func TestPreImportState(t *testing.T) { } } +func TestPreImportState_Plan(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view, TerraformOperationPlan) + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + action, err := h.PreImportState(addr, "test") + + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + result := done(t) + + if got, want := result.Stdout(), "test_instance.foo: Importing state... [id=test]\n"; got != want { + t.Fatalf("unexpected output\n got: %q\nwant: %q", got, want) + } +} + // Test the PostImportState UI hook. Again, this hook behaviour seems odd to // me (see below), so please don't consider these tests as justification for // keeping this behaviour. func TestPostImportState(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := NewView(streams) - h := NewUiHook(view) + h := NewUiHook(view, TerraformOperationImport) addr := addrs.Resource{ Mode: addrs.ManagedResourceMode, diff --git a/internal/command/views/plan.go b/internal/command/views/plan.go index ab1f766e21..f91ad7e993 100644 --- a/internal/command/views/plan.go +++ b/internal/command/views/plan.go @@ -53,7 +53,7 @@ func (v *PlanHuman) Operation() Operation { func (v *PlanHuman) Hooks() []terraform.Hook { return []terraform.Hook{ - NewUiHook(v.view), + NewUiHook(v.view, TerraformOperationPlan), } } diff --git a/internal/command/views/refresh.go b/internal/command/views/refresh.go index 230a48cecc..02bfd01af7 100644 --- a/internal/command/views/refresh.go +++ b/internal/command/views/refresh.go @@ -35,7 +35,6 @@ func NewRefresh(vt arguments.ViewType, view *View) Refresh { return &RefreshHuman{ view: view, inAutomation: view.RunningInAutomation(), - countHook: &countHook{}, } default: panic(fmt.Sprintf("unknown view type %v", vt)) @@ -48,8 +47,6 @@ type RefreshHuman struct { view *View inAutomation bool - - countHook *countHook } var _ Refresh = (*RefreshHuman)(nil) @@ -67,8 +64,7 @@ func (v *RefreshHuman) Operation() Operation { func (v *RefreshHuman) Hooks() []terraform.Hook { return []terraform.Hook{ - v.countHook, - NewUiHook(v.view), + NewUiHook(v.view, TerraformOperationRefresh), } }