Merge pull request #27760 from hashicorp/alisdair/command-views-ui-hook

cli: Migrate Terraform UI hook to command views
This commit is contained in:
Alisdair McDiarmid 2021-02-16 09:36:47 -05:00 committed by GitHub
commit 1ae3d30383
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 177 additions and 114 deletions

View File

@ -183,6 +183,10 @@ type Operation struct {
// configuration from ConfigDir. // configuration from ConfigDir.
ConfigLoader *configload.Loader ConfigLoader *configload.Loader
// Hooks can be used to perform actions triggered by various events during
// the operation's lifecycle.
Hooks []terraform.Hook
// Plan is a plan that was passed as an argument. This is valid for // Plan is a plan that was passed as an argument. This is valid for
// plan and apply arguments but may not work for all backends. // plan and apply arguments but may not work for all backends.
PlanFile *planfile.Reader PlanFile *planfile.Reader

View File

@ -40,12 +40,7 @@ func (b *Local) opApply(
} }
stateHook := new(StateHook) stateHook := new(StateHook)
if b.ContextOpts == nil { op.Hooks = append(op.Hooks, stateHook)
b.ContextOpts = new(terraform.ContextOpts)
}
old := b.ContextOpts.Hooks
defer func() { b.ContextOpts.Hooks = old }()
b.ContextOpts.Hooks = append(b.ContextOpts.Hooks, stateHook)
// Get our context // Get our context
tfCtx, _, opState, contextDiags := b.context(op) tfCtx, _, opState, contextDiags := b.context(op)

View File

@ -77,6 +77,7 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, *configload.
opts.Destroy = op.Destroy opts.Destroy = op.Destroy
opts.Targets = op.Targets opts.Targets = op.Targets
opts.UIInput = op.UIIn opts.UIInput = op.UIIn
opts.Hooks = op.Hooks
opts.SkipRefresh = op.Type != backend.OperationTypeRefresh && !op.PlanRefresh opts.SkipRefresh = op.Type != backend.OperationTypeRefresh && !op.PlanRefresh
if opts.SkipRefresh { if opts.SkipRefresh {

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/plans/planfile" "github.com/hashicorp/terraform/plans/planfile"
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
) )
@ -110,7 +111,6 @@ func (c *ApplyCommand) Run(args []string) int {
// Set up our count hook that keeps track of resource changes // Set up our count hook that keeps track of resource changes
countHook := new(CountHook) countHook := new(CountHook)
c.ExtraHooks = append(c.ExtraHooks, countHook)
// Load the backend // Load the backend
var be backend.Enhanced var be backend.Enhanced
@ -171,6 +171,7 @@ func (c *ApplyCommand) Run(args []string) int {
opReq.AutoApprove = autoApprove opReq.AutoApprove = autoApprove
opReq.ConfigDir = configPath opReq.ConfigDir = configPath
opReq.Destroy = c.Destroy opReq.Destroy = c.Destroy
opReq.Hooks = []terraform.Hook{countHook, c.uiHook()}
opReq.PlanFile = planFile opReq.PlanFile = planFile
opReq.PlanRefresh = refresh opReq.PlanRefresh = refresh
opReq.ShowDiagnostics = c.showDiagnostics opReq.ShowDiagnostics = c.showDiagnostics

View File

@ -941,10 +941,12 @@ func TestApply_planNoModuleFiles(t *testing.T) {
p := applyFixtureProvider() p := applyFixtureProvider()
planPath := applyFixturePlanFile(t) planPath := applyFixturePlanFile(t)
view, _ := testView(t)
apply := &ApplyCommand{ apply := &ApplyCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: new(cli.MockUi), Ui: new(cli.MockUi),
View: view,
}, },
} }
args := []string{ args := []string{

View File

@ -189,6 +189,7 @@ func (c *ImportCommand) Run(args []string) int {
c.showDiagnostics(diags) c.showDiagnostics(diags)
return 1 return 1
} }
opReq.Hooks = []terraform.Hook{c.uiHook()}
{ {
var moreDiags tfdiags.Diagnostics var moreDiags tfdiags.Diagnostics
opReq.Variables, moreDiags = c.collectVariableValues() opReq.Variables, moreDiags = c.collectVariableValues()

View File

@ -24,10 +24,12 @@ func TestImport(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -77,10 +79,12 @@ func TestImport_providerConfig(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -170,9 +174,11 @@ func TestImport_remoteState(t *testing.T) {
// init our backend // init our backend
ui := cli.NewMockUi() ui := cli.NewMockUi()
view, _ := testView(t)
m := Meta{ m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()), testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui, Ui: ui,
View: view,
ProviderSource: providerSource, ProviderSource: providerSource,
} }
@ -192,6 +198,7 @@ func TestImport_remoteState(t *testing.T) {
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -280,9 +287,11 @@ func TestImport_initializationErrorShouldUnlock(t *testing.T) {
// init our backend // init our backend
ui := cli.NewMockUi() ui := cli.NewMockUi()
view, _ := testView(t)
m := Meta{ m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()), testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui, Ui: ui,
View: view,
ProviderSource: providerSource, ProviderSource: providerSource,
} }
@ -305,6 +314,7 @@ func TestImport_initializationErrorShouldUnlock(t *testing.T) {
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -339,10 +349,12 @@ func TestImport_providerConfigWithVar(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -417,10 +429,12 @@ func TestImport_providerConfigWithDataSource(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -480,10 +494,12 @@ func TestImport_providerConfigWithVarDefault(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -557,10 +573,12 @@ func TestImport_providerConfigWithVarFile(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -635,10 +653,12 @@ func TestImport_allowMissingResourceConfig(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -690,10 +710,12 @@ func TestImport_emptyConfig(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -720,10 +742,12 @@ func TestImport_missingResourceConfig(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -750,10 +774,12 @@ func TestImport_missingModuleConfig(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -801,9 +827,11 @@ func TestImportModuleVarFile(t *testing.T) {
// init to install the module // init to install the module
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{ m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()), testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui, Ui: ui,
View: view,
ProviderSource: providerSource, ProviderSource: providerSource,
} }
@ -820,6 +848,7 @@ func TestImportModuleVarFile(t *testing.T) {
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
args := []string{ args := []string{
@ -873,9 +902,11 @@ func TestImportModuleInputVariableEvaluation(t *testing.T) {
// init to install the module // init to install the module
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{ m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()), testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui, Ui: ui,
View: view,
ProviderSource: providerSource, ProviderSource: providerSource,
} }
@ -892,6 +923,7 @@ func TestImportModuleInputVariableEvaluation(t *testing.T) {
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
args := []string{ args := []string{
@ -912,10 +944,12 @@ func TestImport_dataResource(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -942,10 +976,12 @@ func TestImport_invalidResourceAddr(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -972,10 +1008,12 @@ func TestImport_targetIsModule(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{ c := &ImportCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }

View File

@ -21,6 +21,7 @@ import (
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/backend/local" "github.com/hashicorp/terraform/backend/local"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/format" "github.com/hashicorp/terraform/command/format"
"github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/command/webbrowser" "github.com/hashicorp/terraform/command/webbrowser"
@ -69,9 +70,6 @@ type Meta struct {
GlobalPluginDirs []string // Additional paths to search for plugins GlobalPluginDirs []string // Additional paths to search for plugins
Ui cli.Ui // Ui for output Ui cli.Ui // Ui for output
// ExtraHooks are extra hooks to add to the context.
ExtraHooks []terraform.Hook
// Services provides access to remote endpoint information for // Services provides access to remote endpoint information for
// "terraform-native' services running at a specific user-facing hostname. // "terraform-native' services running at a specific user-facing hostname.
Services *disco.Disco Services *disco.Disco
@ -434,8 +432,6 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) {
} }
var opts terraform.ContextOpts var opts terraform.ContextOpts
opts.Hooks = []terraform.Hook{m.uiHook()}
opts.Hooks = append(opts.Hooks, m.ExtraHooks...)
opts.Targets = m.targets opts.Targets = m.targets
opts.UIInput = m.UIInput() opts.UIInput = m.UIInput()
@ -621,15 +617,21 @@ func (m *Meta) process(args []string) []string {
}, },
} }
// Reconfigure the view. This is necessary for commands which use both
// views.View and cli.Ui during the migration phase.
if m.View != nil {
m.View.Configure(&arguments.View{
CompactWarnings: m.compactWarnings,
NoColor: !m.Color,
})
}
return args return args
} }
// uiHook returns the UiHook to use with the context. // uiHook returns the UiHook to use with the context.
func (m *Meta) uiHook() *UiHook { func (m *Meta) uiHook() *views.UiHook {
return &UiHook{ return views.NewUiHook(m.View)
Colorize: m.Colorize(),
Ui: m.Ui,
}
} }
// confirm asks a yes/no confirmation. // confirm asks a yes/no confirmation.

View File

@ -7,6 +7,7 @@ import (
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
) )
@ -82,6 +83,7 @@ func (c *PlanCommand) Run(args []string) int {
opReq := c.Operation(b) opReq := c.Operation(b)
opReq.ConfigDir = configPath opReq.ConfigDir = configPath
opReq.Destroy = destroy opReq.Destroy = destroy
opReq.Hooks = []terraform.Hook{c.uiHook()}
opReq.PlanOutPath = outPath opReq.PlanOutPath = outPath
opReq.PlanRefresh = refresh opReq.PlanRefresh = refresh
opReq.ShowDiagnostics = c.showDiagnostics opReq.ShowDiagnostics = c.showDiagnostics

View File

@ -32,10 +32,12 @@ func TestPlan(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -59,10 +61,12 @@ func TestPlan_lockedState(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -85,10 +89,12 @@ func TestPlan_plan(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -126,10 +132,12 @@ func TestPlan_destroy(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -158,10 +166,12 @@ func TestPlan_noState(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -193,10 +203,12 @@ func TestPlan_outPath(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -246,10 +258,12 @@ func TestPlan_outPathNoChange(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -324,10 +338,12 @@ func TestPlan_outBackend(t *testing.T) {
} }
} }
ui := cli.NewMockUi() ui := cli.NewMockUi()
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -376,10 +392,12 @@ func TestPlan_refreshFalse(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -407,10 +425,12 @@ func TestPlan_state(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -450,10 +470,12 @@ func TestPlan_stateDefault(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -505,10 +527,12 @@ func TestPlan_validate(t *testing.T) {
} }
} }
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -532,10 +556,12 @@ func TestPlan_vars(t *testing.T) {
p := planVarsFixtureProvider() p := planVarsFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -577,10 +603,12 @@ func TestPlan_varsUnset(t *testing.T) {
p := planVarsFixtureProvider() p := planVarsFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -640,10 +668,12 @@ func TestPlan_providerArgumentUnset(t *testing.T) {
}, },
} }
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -667,10 +697,12 @@ func TestPlan_varFile(t *testing.T) {
p := planVarsFixtureProvider() p := planVarsFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -707,10 +739,12 @@ func TestPlan_varFileDefault(t *testing.T) {
p := planVarsFixtureProvider() p := planVarsFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -745,10 +779,12 @@ func TestPlan_varFileWithDecls(t *testing.T) {
p := planVarsFixtureProvider() p := planVarsFixtureProvider()
ui := cli.NewMockUi() ui := cli.NewMockUi()
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -773,10 +809,12 @@ func TestPlan_detailedExitcode(t *testing.T) {
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -794,10 +832,12 @@ func TestPlan_detailedExitcode_emptyDiff(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -819,10 +859,12 @@ func TestPlan_shutdown(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
ShutdownCh: shutdownCh, ShutdownCh: shutdownCh,
}, },
} }
@ -884,10 +926,12 @@ func TestPlan_init_required(t *testing.T) {
defer testChdir(t, td)() defer testChdir(t, td)()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
// Running plan without setting testingOverrides is similar to plan without init // Running plan without setting testingOverrides is similar to plan without init
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -927,10 +971,12 @@ func TestPlan_targeted(t *testing.T) {
} }
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
}, },
} }
@ -961,9 +1007,11 @@ func TestPlan_targetFlagsDiags(t *testing.T) {
defer testChdir(t, td)() defer testChdir(t, td)()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
c := &PlanCommand{ c := &PlanCommand{
Meta: Meta{ Meta: Meta{
Ui: ui, Ui: ui,
View: view,
}, },
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
) )
@ -75,6 +76,7 @@ func (c *RefreshCommand) Run(args []string) int {
// Build the operation // Build the operation
opReq := c.Operation(b) opReq := c.Operation(b)
opReq.ConfigDir = configPath opReq.ConfigDir = configPath
opReq.Hooks = []terraform.Hook{c.uiHook()}
opReq.ShowDiagnostics = c.showDiagnostics opReq.ShowDiagnostics = c.showDiagnostics
opReq.Type = backend.OperationTypeRefresh opReq.Type = backend.OperationTypeRefresh

View File

@ -791,7 +791,7 @@ func TestRefresh_targeted(t *testing.T) {
} }
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t) view, done := testView(t)
c := &RefreshCommand{ c := &RefreshCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
@ -808,7 +808,7 @@ func TestRefresh_targeted(t *testing.T) {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
} }
got := ui.OutputWriter.String() got := done(t).Stdout()
if want := "test_instance.foo: Refreshing"; !strings.Contains(got, want) { if want := "test_instance.foo: Refreshing"; !strings.Contains(got, want) {
t.Fatalf("expected output to contain %q, got:\n%s", want, got) t.Fatalf("expected output to contain %q, got:\n%s", want, got)
} }

View File

@ -255,9 +255,11 @@ func TestShow_json_output(t *testing.T) {
p := showFixtureProvider() p := showFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{ m := Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
Ui: ui, Ui: ui,
View: view,
ProviderSource: providerSource, ProviderSource: providerSource,
} }

View File

@ -1,4 +1,4 @@
package command package views
import ( import (
"bufio" "bufio"
@ -9,8 +9,6 @@ import (
"time" "time"
"unicode" "unicode"
"github.com/mitchellh/cli"
"github.com/mitchellh/colorstring"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
@ -24,17 +22,24 @@ import (
const defaultPeriodicUiTimer = 10 * time.Second const defaultPeriodicUiTimer = 10 * time.Second
const maxIdLen = 80 const maxIdLen = 80
func NewUiHook(view *View) *UiHook {
return &UiHook{
view: view,
periodicUiTimer: defaultPeriodicUiTimer,
resources: make(map[string]uiResourceState),
}
}
type UiHook struct { type UiHook struct {
terraform.NilHook terraform.NilHook
Colorize *colorstring.Colorize view *View
Ui cli.Ui viewLock sync.Mutex
PeriodicUiTimer time.Duration
l sync.Mutex periodicUiTimer time.Duration
once sync.Once
resources map[string]uiResourceState resources map[string]uiResourceState
ui cli.Ui resourcesLock sync.Mutex
} }
var _ terraform.Hook = (*UiHook)(nil) var _ terraform.Hook = (*UiHook)(nil)
@ -63,8 +68,6 @@ const (
) )
func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) {
h.once.Do(h.init)
dispAddr := addr.String() dispAddr := addr.String()
if gen != states.CurrentGen { if gen != states.CurrentGen {
dispAddr = fmt.Sprintf("%s (%s)", dispAddr, gen) dispAddr = fmt.Sprintf("%s (%s)", dispAddr, gen)
@ -89,7 +92,7 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation,
default: default:
// We don't expect any other actions in here, so anything else is a // We don't expect any other actions in here, so anything else is a
// bug in the caller but we'll ignore it in order to be robust. // bug in the caller but we'll ignore it in order to be robust.
h.ui.Output(fmt.Sprintf("(Unknown action %s for %s)", action, dispAddr)) h.println(fmt.Sprintf("(Unknown action %s for %s)", action, dispAddr))
return terraform.HookActionContinue, nil return terraform.HookActionContinue, nil
} }
@ -103,7 +106,7 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation,
idValue = "" idValue = ""
} }
h.ui.Output(h.Colorize.Color(fmt.Sprintf( h.println(h.view.colorize.Color(fmt.Sprintf(
"[reset][bold]%s: %s%s[reset]", "[reset][bold]%s: %s%s[reset]",
dispAddr, dispAddr,
operation, operation,
@ -121,9 +124,9 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation,
done: make(chan struct{}), done: make(chan struct{}),
} }
h.l.Lock() h.resourcesLock.Lock()
h.resources[key] = uiState h.resources[key] = uiState
h.l.Unlock() h.resourcesLock.Unlock()
// Start goroutine that shows progress // Start goroutine that shows progress
go h.stillApplying(uiState) go h.stillApplying(uiState)
@ -138,7 +141,7 @@ func (h *UiHook) stillApplying(state uiResourceState) {
case <-state.DoneCh: case <-state.DoneCh:
return return
case <-time.After(h.PeriodicUiTimer): case <-time.After(h.periodicUiTimer):
// Timer up, show status // Timer up, show status
} }
@ -161,7 +164,7 @@ func (h *UiHook) stillApplying(state uiResourceState) {
idSuffix = fmt.Sprintf("%s=%s, ", state.IDKey, truncateId(state.IDValue, maxIdLen)) idSuffix = fmt.Sprintf("%s=%s, ", state.IDKey, truncateId(state.IDValue, maxIdLen))
} }
h.ui.Output(h.Colorize.Color(fmt.Sprintf( h.println(h.view.colorize.Color(fmt.Sprintf(
"[reset][bold]%s: %s [%s%s elapsed][reset]", "[reset][bold]%s: %s [%s%s elapsed][reset]",
state.DispAddr, state.DispAddr,
msg, msg,
@ -172,17 +175,16 @@ func (h *UiHook) stillApplying(state uiResourceState) {
} }
func (h *UiHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, applyerr error) (terraform.HookAction, error) { func (h *UiHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, applyerr error) (terraform.HookAction, error) {
id := addr.String() id := addr.String()
h.l.Lock() h.resourcesLock.Lock()
state := h.resources[id] state := h.resources[id]
if state.DoneCh != nil { if state.DoneCh != nil {
close(state.DoneCh) close(state.DoneCh)
} }
delete(h.resources, id) delete(h.resources, id)
h.l.Unlock() h.resourcesLock.Unlock()
var stateIdSuffix string var stateIdSuffix string
if k, v := format.ObjectValueID(newState); k != "" && v != "" { if k, v := format.ObjectValueID(newState); k != "" && v != "" {
@ -208,21 +210,17 @@ func (h *UiHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation
return terraform.HookActionContinue, nil return terraform.HookActionContinue, nil
} }
colorized := h.Colorize.Color(fmt.Sprintf( colorized := h.view.colorize.Color(fmt.Sprintf(
"[reset][bold]%s: %s after %s%s[reset]", "[reset][bold]%s: %s after %s%s[reset]",
addr, msg, time.Now().Round(time.Second).Sub(state.Start), stateIdSuffix)) addr, msg, time.Now().Round(time.Second).Sub(state.Start), stateIdSuffix))
h.ui.Output(colorized) h.println(colorized)
return terraform.HookActionContinue, nil return terraform.HookActionContinue, nil
} }
func (h *UiHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (terraform.HookAction, error) {
return terraform.HookActionContinue, nil
}
func (h *UiHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (terraform.HookAction, error) { func (h *UiHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (terraform.HookAction, error) {
h.ui.Output(h.Colorize.Color(fmt.Sprintf( h.println(h.view.colorize.Color(fmt.Sprintf(
"[reset][bold]%s: Provisioning with '%s'...[reset]", "[reset][bold]%s: Provisioning with '%s'...[reset]",
addr, typeName, addr, typeName,
))) )))
@ -231,7 +229,7 @@ func (h *UiHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeNa
func (h *UiHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, msg string) { func (h *UiHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, msg string) {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString(h.Colorize.Color("[reset]")) buf.WriteString(h.view.colorize.Color("[reset]"))
prefix := fmt.Sprintf("%s (%s): ", addr, typeName) prefix := fmt.Sprintf("%s (%s): ", addr, typeName)
s := bufio.NewScanner(strings.NewReader(msg)) s := bufio.NewScanner(strings.NewReader(msg))
@ -243,27 +241,23 @@ func (h *UiHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string
} }
} }
h.ui.Output(strings.TrimSpace(buf.String())) h.println(strings.TrimSpace(buf.String()))
} }
func (h *UiHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (terraform.HookAction, error) { func (h *UiHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (terraform.HookAction, error) {
h.once.Do(h.init)
var stateIdSuffix string var stateIdSuffix string
if k, v := format.ObjectValueID(priorState); k != "" && v != "" { if k, v := format.ObjectValueID(priorState); k != "" && v != "" {
stateIdSuffix = fmt.Sprintf(" [%s=%s]", k, v) stateIdSuffix = fmt.Sprintf(" [%s=%s]", k, v)
} }
h.ui.Output(h.Colorize.Color(fmt.Sprintf( h.println(h.view.colorize.Color(fmt.Sprintf(
"[reset][bold]%s: Refreshing state...%s", "[reset][bold]%s: Refreshing state...%s",
addr, stateIdSuffix))) addr, stateIdSuffix)))
return terraform.HookActionContinue, nil return terraform.HookActionContinue, nil
} }
func (h *UiHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (terraform.HookAction, error) { func (h *UiHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (terraform.HookAction, error) {
h.once.Do(h.init) h.println(h.view.colorize.Color(fmt.Sprintf(
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
"[reset][bold]%s: Importing from ID %q...", "[reset][bold]%s: Importing from ID %q...",
addr, importID, addr, importID,
))) )))
@ -271,12 +265,10 @@ func (h *UiHook) PreImportState(addr addrs.AbsResourceInstance, importID string)
} }
func (h *UiHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (terraform.HookAction, error) { func (h *UiHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (terraform.HookAction, error) {
h.once.Do(h.init) h.println(h.view.colorize.Color(fmt.Sprintf(
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
"[reset][bold][green]%s: Import prepared!", addr))) "[reset][bold][green]%s: Import prepared!", addr)))
for _, s := range imported { for _, s := range imported {
h.ui.Output(h.Colorize.Color(fmt.Sprintf( h.println(h.view.colorize.Color(fmt.Sprintf(
"[reset][green] Prepared %s for import", "[reset][green] Prepared %s for import",
s.TypeName, s.TypeName,
))) )))
@ -285,19 +277,11 @@ func (h *UiHook) PostImportState(addr addrs.AbsResourceInstance, imported []prov
return terraform.HookActionContinue, nil return terraform.HookActionContinue, nil
} }
func (h *UiHook) init() { // Wrap calls to the view so that concurrent calls do not interleave println.
if h.Colorize == nil { func (h *UiHook) println(s string) {
panic("colorize not given") h.viewLock.Lock()
} defer h.viewLock.Unlock()
if h.PeriodicUiTimer == 0 { h.view.streams.Println(s)
h.PeriodicUiTimer = defaultPeriodicUiTimer
}
h.resources = make(map[string]uiResourceState)
// Wrap the ui so that it is safe for concurrency regardless of the
// underlying reader/writer that is in place.
h.ui = &cli.ConcurrentUi{Ui: h.Ui}
} }
// scanLines is basically copied from the Go standard library except // scanLines is basically copied from the Go standard library except

View File

@ -1,4 +1,4 @@
package command package views
import ( import (
"fmt" "fmt"
@ -6,28 +6,20 @@ import (
"testing" "testing"
"time" "time"
"github.com/mitchellh/cli"
"github.com/mitchellh/colorstring"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
func TestUiHookPreApply_periodicTimer(t *testing.T) { func TestUiHookPreApply_periodicTimer(t *testing.T) {
ui := cli.NewMockUi() streams, done := terminal.StreamsForTesting(t)
h := &UiHook{ view := NewView(streams)
Colorize: &colorstring.Colorize{ h := NewUiHook(view)
Colors: colorstring.DefaultColors, h.periodicUiTimer = 1 * time.Second
Disable: true,
Reset: true,
},
Ui: ui,
PeriodicUiTimer: 1 * time.Second,
}
h.init()
h.resources = map[string]uiResourceState{ h.resources = map[string]uiResourceState{
"data.aws_availability_zones.available": uiResourceState{ "data.aws_availability_zones.available": uiResourceState{
Op: uiResourceDestroy, Op: uiResourceDestroy,
@ -75,29 +67,23 @@ data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:
data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 2s elapsed] data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 2s elapsed]
data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 3s elapsed] data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 3s elapsed]
` `
output := ui.OutputWriter.String() result := done(t)
output := result.Stdout()
if output != expectedOutput { if output != expectedOutput {
t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output) t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output)
} }
expectedErrOutput := "" expectedErrOutput := ""
errOutput := ui.ErrorWriter.String() errOutput := result.Stderr()
if errOutput != expectedErrOutput { if errOutput != expectedErrOutput {
t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput)
} }
} }
func TestUiHookPreApply_destroy(t *testing.T) { func TestUiHookPreApply_destroy(t *testing.T) {
ui := cli.NewMockUi() streams, done := terminal.StreamsForTesting(t)
h := &UiHook{ view := NewView(streams)
Colorize: &colorstring.Colorize{ h := NewUiHook(view)
Colors: colorstring.DefaultColors,
Disable: true,
Reset: true,
},
Ui: ui,
}
h.init()
h.resources = map[string]uiResourceState{ h.resources = map[string]uiResourceState{
"data.aws_availability_zones.available": uiResourceState{ "data.aws_availability_zones.available": uiResourceState{
Op: uiResourceDestroy, Op: uiResourceDestroy,
@ -138,30 +124,24 @@ func TestUiHookPreApply_destroy(t *testing.T) {
close(uiState.DoneCh) close(uiState.DoneCh)
<-uiState.done <-uiState.done
result := done(t)
expectedOutput := "data.aws_availability_zones.available: Destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC]\n" expectedOutput := "data.aws_availability_zones.available: Destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC]\n"
output := ui.OutputWriter.String() output := result.Stdout()
if output != expectedOutput { if output != expectedOutput {
t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output) t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output)
} }
expectedErrOutput := "" expectedErrOutput := ""
errOutput := ui.ErrorWriter.String() errOutput := result.Stderr()
if errOutput != expectedErrOutput { if errOutput != expectedErrOutput {
t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput)
} }
} }
func TestUiHookPostApply_emptyState(t *testing.T) { func TestUiHookPostApply_emptyState(t *testing.T) {
ui := cli.NewMockUi() streams, done := terminal.StreamsForTesting(t)
h := &UiHook{ view := NewView(streams)
Colorize: &colorstring.Colorize{ h := NewUiHook(view)
Colors: colorstring.DefaultColors,
Disable: true,
Reset: true,
},
Ui: ui,
}
h.init()
h.resources = map[string]uiResourceState{ h.resources = map[string]uiResourceState{
"data.google_compute_zones.available": uiResourceState{ "data.google_compute_zones.available": uiResourceState{
Op: uiResourceDestroy, Op: uiResourceDestroy,
@ -187,15 +167,16 @@ func TestUiHookPostApply_emptyState(t *testing.T) {
if action != terraform.HookActionContinue { if action != terraform.HookActionContinue {
t.Fatalf("Expected hook to continue, given: %#v", action) t.Fatalf("Expected hook to continue, given: %#v", action)
} }
result := done(t)
expectedRegexp := "^data.google_compute_zones.available: Destruction complete after -?[a-z0-9µ.]+\n$" expectedRegexp := "^data.google_compute_zones.available: Destruction complete after -?[a-z0-9µ.]+\n$"
output := ui.OutputWriter.String() output := result.Stdout()
if matched, _ := regexp.MatchString(expectedRegexp, output); !matched { if matched, _ := regexp.MatchString(expectedRegexp, output); !matched {
t.Fatalf("Output didn't match regexp.\nExpected: %q\nGiven: %q", expectedRegexp, output) t.Fatalf("Output didn't match regexp.\nExpected: %q\nGiven: %q", expectedRegexp, output)
} }
expectedErrOutput := "" expectedErrOutput := ""
errOutput := ui.ErrorWriter.String() errOutput := result.Stderr()
if errOutput != expectedErrOutput { if errOutput != expectedErrOutput {
t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput)
} }