Merge pull request #27929 from hashicorp/alisdair/command-views-cleanup
Some command views cleanup
This commit is contained in:
commit
37fff9336a
|
@ -216,9 +216,6 @@ type Operation struct {
|
|||
UIIn terraform.UIInput
|
||||
UIOut terraform.UIOutput
|
||||
|
||||
// ShowDiagnostics prints diagnostic messages to the UI.
|
||||
ShowDiagnostics func(vals ...interface{})
|
||||
|
||||
// StateLocker is used to lock the state while providing UI feedback to the
|
||||
// user. This will be replaced by the Backend to update the context.
|
||||
//
|
||||
|
@ -253,7 +250,7 @@ func (o *Operation) Config() (*configs.Config, tfdiags.Diagnostics) {
|
|||
//
|
||||
// If the given diagnostics contains errors then the operation's result
|
||||
// will be set to backend.OperationFailure. It will be set to
|
||||
// backend.OperationSuccess otherwise. It will then use b.ShowDiagnostics
|
||||
// backend.OperationSuccess otherwise. It will then use o.View.Diagnostics
|
||||
// to show the given diagnostics before returning.
|
||||
//
|
||||
// Callers should feel free to do each of these operations separately in
|
||||
|
@ -266,14 +263,14 @@ func (o *Operation) ReportResult(op *RunningOperation, diags tfdiags.Diagnostics
|
|||
} else {
|
||||
op.Result = OperationSuccess
|
||||
}
|
||||
if o.ShowDiagnostics != nil {
|
||||
o.ShowDiagnostics(diags)
|
||||
if o.View != nil {
|
||||
o.View.Diagnostics(diags)
|
||||
} else {
|
||||
// Shouldn't generally happen, but if it does then we'll at least
|
||||
// make some noise in the logs to help us spot it.
|
||||
if len(diags) != 0 {
|
||||
log.Printf(
|
||||
"[ERROR] Backend needs to report diagnostics but ShowDiagnostics is not set:\n%s",
|
||||
"[ERROR] Backend needs to report diagnostics but View is not set:\n%s",
|
||||
diags.ErrWithWarnings(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -56,10 +56,6 @@ type CLIOpts struct {
|
|||
// for tailoring the output to fit the attached terminal, for example.
|
||||
Streams *terminal.Streams
|
||||
|
||||
// ShowDiagnostics is a function that will format and print diagnostic
|
||||
// messages to the UI.
|
||||
ShowDiagnostics func(vals ...interface{})
|
||||
|
||||
// StatePath is the local path where state is read from.
|
||||
//
|
||||
// StateOutPath is the local path where the state will be written.
|
||||
|
|
|
@ -53,7 +53,7 @@ func (b *Local) opApply(
|
|||
defer func() {
|
||||
diags := op.StateLocker.Unlock()
|
||||
if diags.HasErrors() {
|
||||
op.ShowDiagnostics(diags)
|
||||
op.View.Diagnostics(diags)
|
||||
runningOp.Result = backend.OperationFailure
|
||||
}
|
||||
}()
|
||||
|
@ -101,7 +101,7 @@ func (b *Local) opApply(
|
|||
// We'll show any accumulated warnings before we display the prompt,
|
||||
// so the user can consider them when deciding how to answer.
|
||||
if len(diags) > 0 {
|
||||
op.ShowDiagnostics(diags)
|
||||
op.View.Diagnostics(diags)
|
||||
diags = nil // reset so we won't show the same diagnostics again later
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ func (b *Local) opApply(
|
|||
// If we've accumulated any warnings along the way then we'll show them
|
||||
// here just before we show the summary and next steps. If we encountered
|
||||
// errors then we would've returned early at some other point above.
|
||||
op.ShowDiagnostics(diags)
|
||||
op.View.Diagnostics(diags)
|
||||
}
|
||||
|
||||
// backupStateForError is called in a scenario where we're unable to persist the
|
||||
|
|
|
@ -101,8 +101,8 @@ func TestLocal_applyEmptyDir(t *testing.T) {
|
|||
// the backend should be unlocked after a run
|
||||
assertBackendStateUnlocked(t, b)
|
||||
|
||||
if errOutput := done(t).Stderr(); errOutput != "" {
|
||||
t.Fatalf("unexpected error output:\n%s", errOutput)
|
||||
if got, want := done(t).Stderr(), "Error: No configuration files"; !strings.Contains(got, want) {
|
||||
t.Fatalf("unexpected error output:\n%s\nwant: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ func TestLocal_applyError(t *testing.T) {
|
|||
ami := r.Config.GetAttr("ami").AsString()
|
||||
if !errored && ami == "error" {
|
||||
errored = true
|
||||
diags = diags.Append(errors.New("error"))
|
||||
diags = diags.Append(errors.New("ami error"))
|
||||
return providers.ApplyResourceChangeResponse{
|
||||
Diagnostics: diags,
|
||||
}
|
||||
|
@ -201,8 +201,8 @@ test_instance.foo:
|
|||
// the backend should be unlocked after a run
|
||||
assertBackendStateUnlocked(t, b)
|
||||
|
||||
if errOutput := done(t).Stderr(); errOutput != "" {
|
||||
t.Fatalf("unexpected error output:\n%s", errOutput)
|
||||
if got, want := done(t).Stderr(), "Error: ami error"; !strings.Contains(got, want) {
|
||||
t.Fatalf("unexpected error output:\n%s\nwant: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,9 +229,6 @@ func TestLocal_applyBackendFail(t *testing.T) {
|
|||
op, configCleanup, done := testOperationApply(t, wd+"/testdata/apply")
|
||||
defer configCleanup()
|
||||
|
||||
record, playback := testRecordDiagnostics(t)
|
||||
op.ShowDiagnostics = record
|
||||
|
||||
b.Backend = &backendWithFailingState{}
|
||||
|
||||
run, err := b.Operation(context.Background(), op)
|
||||
|
@ -239,11 +236,14 @@ func TestLocal_applyBackendFail(t *testing.T) {
|
|||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
<-run.Done()
|
||||
|
||||
output := done(t)
|
||||
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatalf("apply succeeded; want error")
|
||||
}
|
||||
|
||||
diagErr := playback().Err().Error()
|
||||
diagErr := output.Stderr()
|
||||
if !strings.Contains(diagErr, "Error saving state: fake failure") {
|
||||
t.Fatalf("missing \"fake failure\" message in diags:\n%s", diagErr)
|
||||
}
|
||||
|
@ -259,10 +259,6 @@ test_instance.foo:
|
|||
|
||||
// the backend should be unlocked after a run
|
||||
assertBackendStateUnlocked(t, b)
|
||||
|
||||
if errOutput := done(t).Stderr(); errOutput != "" {
|
||||
t.Fatalf("unexpected error output:\n%s", errOutput)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocal_applyRefreshFalse(t *testing.T) {
|
||||
|
@ -323,7 +319,6 @@ func testOperationApply(t *testing.T, configDir string) (*backend.Operation, fun
|
|||
Type: backend.OperationTypeApply,
|
||||
ConfigDir: configDir,
|
||||
ConfigLoader: configLoader,
|
||||
ShowDiagnostics: testLogDiagnostics(t),
|
||||
StateLocker: clistate.NewNoopLocker(),
|
||||
View: view,
|
||||
}, configCleanup, done
|
||||
|
|
|
@ -64,7 +64,7 @@ func (b *Local) opPlan(
|
|||
defer func() {
|
||||
diags := op.StateLocker.Unlock()
|
||||
if diags.HasErrors() {
|
||||
op.ShowDiagnostics(diags)
|
||||
op.View.Diagnostics(diags)
|
||||
runningOp.Result = backend.OperationFailure
|
||||
}
|
||||
}()
|
||||
|
@ -135,7 +135,7 @@ func (b *Local) opPlan(
|
|||
op.View.PlanNoChanges()
|
||||
|
||||
// Even if there are no changes, there still could be some warnings
|
||||
op.ShowDiagnostics(diags)
|
||||
op.View.Diagnostics(diags)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ func (b *Local) opPlan(
|
|||
// If we've accumulated any warnings along the way then we'll show them
|
||||
// here just before we show the summary and next steps. If we encountered
|
||||
// errors then we would've returned early at some other point above.
|
||||
op.ShowDiagnostics(diags)
|
||||
op.View.Diagnostics(diags)
|
||||
|
||||
op.View.PlanNextStep(op.PlanOutPath)
|
||||
}
|
||||
|
|
|
@ -90,8 +90,6 @@ func TestLocal_planNoConfig(t *testing.T) {
|
|||
TestLocalProvider(t, b, "test", &terraform.ProviderSchema{})
|
||||
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/empty")
|
||||
record, playback := testRecordDiagnostics(t)
|
||||
op.ShowDiagnostics = record
|
||||
defer configCleanup()
|
||||
op.PlanRefresh = true
|
||||
|
||||
|
@ -101,21 +99,18 @@ func TestLocal_planNoConfig(t *testing.T) {
|
|||
}
|
||||
<-run.Done()
|
||||
|
||||
output := done(t)
|
||||
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("plan operation succeeded; want failure")
|
||||
}
|
||||
|
||||
output := playback().Err().Error()
|
||||
if !strings.Contains(output, "No configuration files") {
|
||||
t.Fatalf("bad: %s", err)
|
||||
if stderr := output.Stderr(); !strings.Contains(stderr, "No configuration files") {
|
||||
t.Fatalf("bad: %s", stderr)
|
||||
}
|
||||
|
||||
// the backend should be unlocked after a run
|
||||
assertBackendStateUnlocked(t, b)
|
||||
|
||||
if errOutput := done(t).Stderr(); errOutput != "" {
|
||||
t.Fatalf("unexpected error output:\n%s", errOutput)
|
||||
}
|
||||
}
|
||||
|
||||
// This test validates the state lacking behavior when the inner call to
|
||||
|
@ -141,8 +136,8 @@ func TestLocal_plan_context_error(t *testing.T) {
|
|||
// the backend should be unlocked after a run
|
||||
assertBackendStateUnlocked(t, b)
|
||||
|
||||
if errOutput := done(t).Stderr(); errOutput != "" {
|
||||
t.Fatalf("unexpected error output:\n%s", errOutput)
|
||||
if got, want := done(t).Stderr(), "Error: Could not load plugin"; !strings.Contains(got, want) {
|
||||
t.Fatalf("unexpected error output:\n%s\nwant: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -726,7 +721,6 @@ func testOperationPlan(t *testing.T, configDir string) (*backend.Operation, func
|
|||
Type: backend.OperationTypePlan,
|
||||
ConfigDir: configDir,
|
||||
ConfigLoader: configLoader,
|
||||
ShowDiagnostics: testLogDiagnostics(t),
|
||||
StateLocker: clistate.NewNoopLocker(),
|
||||
View: view,
|
||||
}, configCleanup, done
|
||||
|
|
|
@ -57,7 +57,7 @@ func (b *Local) opRefresh(
|
|||
defer func() {
|
||||
diags := op.StateLocker.Unlock()
|
||||
if diags.HasErrors() {
|
||||
op.ShowDiagnostics(diags)
|
||||
op.View.Diagnostics(diags)
|
||||
runningOp.Result = backend.OperationFailure
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -238,10 +238,6 @@ func TestLocal_refreshEmptyState(t *testing.T) {
|
|||
|
||||
op, configCleanup, done := testOperationRefresh(t, "./testdata/refresh")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
record, playback := testRecordDiagnostics(t)
|
||||
op.ShowDiagnostics = record
|
||||
|
||||
run, err := b.Operation(context.Background(), op)
|
||||
if err != nil {
|
||||
|
@ -249,11 +245,12 @@ func TestLocal_refreshEmptyState(t *testing.T) {
|
|||
}
|
||||
<-run.Done()
|
||||
|
||||
diags := playback()
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("expected only warning diags, got errors: %s", diags.Err())
|
||||
output := done(t)
|
||||
|
||||
if stderr := output.Stderr(); stderr != "" {
|
||||
t.Fatalf("expected only warning diags, got errors: %s", stderr)
|
||||
}
|
||||
if got, want := diags.ErrWithWarnings().Error(), "Empty or non-existent state"; !strings.Contains(got, want) {
|
||||
if got, want := output.Stdout(), "Warning: Empty or non-existent state"; !strings.Contains(got, want) {
|
||||
t.Errorf("wrong diags\n got: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
|
@ -273,7 +270,6 @@ func testOperationRefresh(t *testing.T, configDir string) (*backend.Operation, f
|
|||
Type: backend.OperationTypeRefresh,
|
||||
ConfigDir: configDir,
|
||||
ConfigLoader: configLoader,
|
||||
ShowDiagnostics: testLogDiagnostics(t),
|
||||
StateLocker: clistate.NewNoopLocker(),
|
||||
View: view,
|
||||
}, configCleanup, done
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/states/statemgr"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
// TestLocal returns a configured Local struct with temporary paths and
|
||||
|
@ -245,43 +244,3 @@ func assertBackendStateLocked(t *testing.T, b *Local) bool {
|
|||
t.Error("unexpected success locking state")
|
||||
return true
|
||||
}
|
||||
|
||||
// testRecordDiagnostics allows tests to record and later inspect diagnostics
|
||||
// emitted during an Operation. It returns a record function which can be set
|
||||
// as the ShowDiagnostics value of an Operation, and a playback function which
|
||||
// returns the recorded diagnostics for inspection.
|
||||
func testRecordDiagnostics(t *testing.T) (record func(vals ...interface{}), playback func() tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
record = func(vals ...interface{}) {
|
||||
diags = diags.Append(vals...)
|
||||
}
|
||||
playback = func() tfdiags.Diagnostics {
|
||||
diags.Sort()
|
||||
return diags
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// testLogDiagnostics returns a function which can be used as the
|
||||
// ShowDiagnostics value for an Operation, in order to help debugging during
|
||||
// tests. Any calls to this function result in test logs.
|
||||
func testLogDiagnostics(t *testing.T) func(vals ...interface{}) {
|
||||
return func(vals ...interface{}) {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(vals...)
|
||||
diags.Sort()
|
||||
|
||||
for _, diag := range diags {
|
||||
// NOTE: Since the caller here is not directly the TestLocal
|
||||
// function, t.Helper doesn't apply and so the log source
|
||||
// isn't correctly shown in the test log output. This seems
|
||||
// unavoidable as long as this is happening so indirectly.
|
||||
desc := diag.Description()
|
||||
if desc.Detail != "" {
|
||||
t.Logf("%s: %s", desc.Summary, desc.Detail)
|
||||
} else {
|
||||
t.Log(desc.Summary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,53 +22,44 @@ import (
|
|||
"github.com/hashicorp/terraform/plans/planfile"
|
||||
"github.com/hashicorp/terraform/states/statemgr"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
tfversion "github.com/hashicorp/terraform/version"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
func testOperationApply(t *testing.T, configDir string) (*backend.Operation, func()) {
|
||||
func testOperationApply(t *testing.T, configDir string) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) {
|
||||
t.Helper()
|
||||
|
||||
return testOperationApplyWithTimeout(t, configDir, 0)
|
||||
}
|
||||
|
||||
func testOperationApplyWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backend.Operation, func()) {
|
||||
func testOperationApplyWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) {
|
||||
t.Helper()
|
||||
|
||||
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir)
|
||||
|
||||
streams, _ := terminal.StreamsForTesting(t)
|
||||
view := views.NewStateLocker(arguments.ViewHuman, views.NewView(streams))
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
view := views.NewView(streams)
|
||||
stateLockerView := views.NewStateLocker(arguments.ViewHuman, view)
|
||||
operationView := views.NewOperation(arguments.ViewHuman, false, view)
|
||||
|
||||
return &backend.Operation{
|
||||
ConfigDir: configDir,
|
||||
ConfigLoader: configLoader,
|
||||
Parallelism: defaultParallelism,
|
||||
PlanRefresh: true,
|
||||
ShowDiagnostics: testLogDiagnostics(t),
|
||||
StateLocker: clistate.NewLocker(timeout, view),
|
||||
StateLocker: clistate.NewLocker(timeout, stateLockerView),
|
||||
Type: backend.OperationTypeApply,
|
||||
}, configCleanup
|
||||
}
|
||||
|
||||
func testOperationApplyWithDiagnostics(t *testing.T, configDir string) (*backend.Operation, func(), func() tfdiags.Diagnostics) {
|
||||
t.Helper()
|
||||
|
||||
op, cleanup := testOperationApply(t, configDir)
|
||||
|
||||
record, playback := testRecordDiagnostics(t)
|
||||
op.ShowDiagnostics = record
|
||||
|
||||
return op, cleanup, playback
|
||||
View: operationView,
|
||||
}, configCleanup, done
|
||||
}
|
||||
|
||||
func TestRemote_applyBasic(t *testing.T) {
|
||||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "yes",
|
||||
|
@ -117,8 +108,9 @@ func TestRemote_applyCanceled(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -158,7 +150,7 @@ func TestRemote_applyWithoutPermissions(t *testing.T) {
|
|||
}
|
||||
w.Permissions.CanQueueApply = false
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
|
||||
op.UIOut = b.CLI
|
||||
|
@ -170,11 +162,12 @@ func TestRemote_applyWithoutPermissions(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "Insufficient rights to apply changes") {
|
||||
t.Fatalf("expected a permissions error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -197,7 +190,7 @@ func TestRemote_applyWithVCS(t *testing.T) {
|
|||
t.Fatalf("error creating named workspace: %v", err)
|
||||
}
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
|
||||
op.Workspace = "prod"
|
||||
|
@ -208,6 +201,7 @@ func TestRemote_applyWithVCS(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
@ -215,7 +209,7 @@ func TestRemote_applyWithVCS(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "not allowed for workspaces with a VCS") {
|
||||
t.Fatalf("expected a VCS error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -225,7 +219,7 @@ func TestRemote_applyWithParallelism(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
|
||||
op.Parallelism = 3
|
||||
|
@ -237,11 +231,12 @@ func TestRemote_applyWithParallelism(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "parallelism values are currently not supported") {
|
||||
t.Fatalf("expected a parallelism error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -251,7 +246,7 @@ func TestRemote_applyWithPlan(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
|
||||
op.PlanFile = &planfile.Reader{}
|
||||
|
@ -263,6 +258,7 @@ func TestRemote_applyWithPlan(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
@ -270,7 +266,7 @@ func TestRemote_applyWithPlan(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "saved plan is currently not supported") {
|
||||
t.Fatalf("expected a saved plan error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -280,7 +276,7 @@ func TestRemote_applyWithoutRefresh(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
|
||||
op.PlanRefresh = false
|
||||
|
@ -292,11 +288,12 @@ func TestRemote_applyWithoutRefresh(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "refresh is currently not supported") {
|
||||
t.Fatalf("expected a refresh error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -306,8 +303,9 @@ func TestRemote_applyWithTarget(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
addr, _ := addrs.ParseAbsResourceStr("null_resource.foo")
|
||||
|
||||
|
@ -344,7 +342,7 @@ func TestRemote_applyWithTargetIncompatibleAPIVersion(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
|
||||
// Set the tfe client's RemoteAPIVersion to an empty string, to mimic
|
||||
|
@ -362,6 +360,7 @@ func TestRemote_applyWithTargetIncompatibleAPIVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
@ -369,7 +368,7 @@ func TestRemote_applyWithTargetIncompatibleAPIVersion(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "Resource targeting is not supported") {
|
||||
t.Fatalf("expected a targeting error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -379,7 +378,7 @@ func TestRemote_applyWithVariables(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply-variables")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply-variables")
|
||||
defer configCleanup()
|
||||
|
||||
op.Variables = testVariables(terraform.ValueFromNamedFile, "foo", "bar")
|
||||
|
@ -391,11 +390,12 @@ func TestRemote_applyWithVariables(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "variables are currently not supported") {
|
||||
t.Fatalf("expected a variables error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ func TestRemote_applyNoConfig(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/empty")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/empty")
|
||||
defer configCleanup()
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
@ -416,6 +416,7 @@ func TestRemote_applyNoConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
@ -423,7 +424,7 @@ func TestRemote_applyNoConfig(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "configuration files found") {
|
||||
t.Fatalf("expected configuration files error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -439,8 +440,9 @@ func TestRemote_applyNoChanges(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply-no-changes")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply-no-changes")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -470,7 +472,7 @@ func TestRemote_applyNoApprove(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
|
@ -487,6 +489,7 @@ func TestRemote_applyNoApprove(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
@ -498,7 +501,7 @@ func TestRemote_applyNoApprove(t *testing.T) {
|
|||
t.Fatalf("expected no unused answers, got: %v", input.answers)
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "Apply discarded") {
|
||||
t.Fatalf("expected an apply discarded error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -508,8 +511,9 @@ func TestRemote_applyAutoApprove(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "no",
|
||||
|
@ -553,8 +557,9 @@ func TestRemote_applyApprovedExternally(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "wait-for-external-update",
|
||||
|
@ -628,8 +633,9 @@ func TestRemote_applyDiscardedExternally(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "wait-for-external-update",
|
||||
|
@ -716,8 +722,9 @@ func TestRemote_applyWithAutoApply(t *testing.T) {
|
|||
t.Fatalf("error creating named workspace: %v", err)
|
||||
}
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "yes",
|
||||
|
@ -767,8 +774,9 @@ func TestRemote_applyForceLocal(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "yes",
|
||||
|
@ -829,8 +837,9 @@ func TestRemote_applyWorkspaceWithoutOperations(t *testing.T) {
|
|||
t.Fatalf("error creating named workspace: %v", err)
|
||||
}
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "yes",
|
||||
|
@ -900,8 +909,9 @@ func TestRemote_applyLockTimeout(t *testing.T) {
|
|||
t.Fatalf("error creating pending run: %v", err)
|
||||
}
|
||||
|
||||
op, configCleanup := testOperationApplyWithTimeout(t, "./testdata/apply", 50*time.Millisecond)
|
||||
op, configCleanup, done := testOperationApplyWithTimeout(t, "./testdata/apply", 50*time.Millisecond)
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"cancel": "yes",
|
||||
|
@ -950,8 +960,9 @@ func TestRemote_applyDestroy(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply-destroy")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply-destroy")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "yes",
|
||||
|
@ -999,8 +1010,9 @@ func TestRemote_applyDestroyNoConfig(t *testing.T) {
|
|||
"approve": "yes",
|
||||
})
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/empty")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/empty")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Destroy = true
|
||||
op.UIIn = input
|
||||
|
@ -1029,8 +1041,9 @@ func TestRemote_applyPolicyPass(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply-policy-passed")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-passed")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "yes",
|
||||
|
@ -1076,7 +1089,7 @@ func TestRemote_applyPolicyHardFail(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply-policy-hard-failed")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-hard-failed")
|
||||
defer configCleanup()
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
|
@ -1093,6 +1106,7 @@ func TestRemote_applyPolicyHardFail(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
viewOutput := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
@ -1104,7 +1118,7 @@ func TestRemote_applyPolicyHardFail(t *testing.T) {
|
|||
t.Fatalf("expected an unused answers, got: %v", input.answers)
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := viewOutput.Stderr()
|
||||
if !strings.Contains(errOutput, "hard failed") {
|
||||
t.Fatalf("expected a policy check error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -1128,8 +1142,9 @@ func TestRemote_applyPolicySoftFail(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply-policy-soft-failed")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-soft-failed")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"override": "override",
|
||||
|
@ -1176,7 +1191,7 @@ func TestRemote_applyPolicySoftFailAutoApprove(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply-policy-soft-failed")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-soft-failed")
|
||||
defer configCleanup()
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
|
@ -1194,6 +1209,7 @@ func TestRemote_applyPolicySoftFailAutoApprove(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
viewOutput := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected apply operation to fail")
|
||||
}
|
||||
|
@ -1205,7 +1221,7 @@ func TestRemote_applyPolicySoftFailAutoApprove(t *testing.T) {
|
|||
t.Fatalf("expected an unused answers, got: %v", input.answers)
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := viewOutput.Stderr()
|
||||
if !strings.Contains(errOutput, "soft failed") {
|
||||
t.Fatalf("expected a policy check error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -1242,8 +1258,9 @@ func TestRemote_applyPolicySoftFailAutoApply(t *testing.T) {
|
|||
t.Fatalf("error creating named workspace: %v", err)
|
||||
}
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply-policy-soft-failed")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-soft-failed")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"override": "override",
|
||||
|
@ -1290,8 +1307,9 @@ func TestRemote_applyWithRemoteError(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationApply(t, "./testdata/apply-with-error")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply-with-error")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -1393,13 +1411,12 @@ func TestRemote_applyVersionCheck(t *testing.T) {
|
|||
}
|
||||
|
||||
// RUN: prepare the apply operation and run it
|
||||
op, configCleanup, playback := testOperationApplyWithDiagnostics(t, "./testdata/apply")
|
||||
op, configCleanup, done := testOperationApply(t, "./testdata/apply")
|
||||
defer configCleanup()
|
||||
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams))
|
||||
op.View = view
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"approve": "yes",
|
||||
|
@ -1416,6 +1433,7 @@ func TestRemote_applyVersionCheck(t *testing.T) {
|
|||
|
||||
// RUN: wait for completion
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
|
||||
if tc.wantErr != "" {
|
||||
// ASSERT: if the test case wants an error, check for failure
|
||||
|
@ -1423,7 +1441,7 @@ func TestRemote_applyVersionCheck(t *testing.T) {
|
|||
if run.Result != backend.OperationFailure {
|
||||
t.Fatalf("expected run to fail, but result was %#v", run.Result)
|
||||
}
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, tc.wantErr) {
|
||||
t.Fatalf("missing error %q\noutput: %s", tc.wantErr, errOutput)
|
||||
}
|
||||
|
|
|
@ -21,52 +21,43 @@ import (
|
|||
"github.com/hashicorp/terraform/plans/planfile"
|
||||
"github.com/hashicorp/terraform/states/statemgr"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
func testOperationPlan(t *testing.T, configDir string) (*backend.Operation, func()) {
|
||||
func testOperationPlan(t *testing.T, configDir string) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) {
|
||||
t.Helper()
|
||||
|
||||
return testOperationPlanWithTimeout(t, configDir, 0)
|
||||
}
|
||||
|
||||
func testOperationPlanWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backend.Operation, func()) {
|
||||
func testOperationPlanWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) {
|
||||
t.Helper()
|
||||
|
||||
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir)
|
||||
|
||||
streams, _ := terminal.StreamsForTesting(t)
|
||||
view := views.NewStateLocker(arguments.ViewHuman, views.NewView(streams))
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
view := views.NewView(streams)
|
||||
stateLockerView := views.NewStateLocker(arguments.ViewHuman, view)
|
||||
operationView := views.NewOperation(arguments.ViewHuman, false, view)
|
||||
|
||||
return &backend.Operation{
|
||||
ConfigDir: configDir,
|
||||
ConfigLoader: configLoader,
|
||||
Parallelism: defaultParallelism,
|
||||
PlanRefresh: true,
|
||||
ShowDiagnostics: testLogDiagnostics(t),
|
||||
StateLocker: clistate.NewLocker(timeout, view),
|
||||
StateLocker: clistate.NewLocker(timeout, stateLockerView),
|
||||
Type: backend.OperationTypePlan,
|
||||
}, configCleanup
|
||||
}
|
||||
|
||||
func testOperationPlanWithDiagnostics(t *testing.T, configDir string) (*backend.Operation, func(), func() tfdiags.Diagnostics) {
|
||||
t.Helper()
|
||||
|
||||
op, cleanup := testOperationPlan(t, configDir)
|
||||
|
||||
record, playback := testRecordDiagnostics(t)
|
||||
op.ShowDiagnostics = record
|
||||
|
||||
return op, cleanup, playback
|
||||
View: operationView,
|
||||
}, configCleanup, done
|
||||
}
|
||||
|
||||
func TestRemote_planBasic(t *testing.T) {
|
||||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -102,8 +93,9 @@ func TestRemote_planCanceled(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -131,8 +123,9 @@ func TestRemote_planLongLine(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan-long-line")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan-long-line")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -175,7 +168,7 @@ func TestRemote_planWithoutPermissions(t *testing.T) {
|
|||
}
|
||||
w.Permissions.CanQueueRun = false
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
|
||||
op.Workspace = "prod"
|
||||
|
@ -186,11 +179,12 @@ func TestRemote_planWithoutPermissions(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "Insufficient rights to generate a plan") {
|
||||
t.Fatalf("expected a permissions error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -200,7 +194,7 @@ func TestRemote_planWithParallelism(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
|
||||
op.Parallelism = 3
|
||||
|
@ -212,11 +206,12 @@ func TestRemote_planWithParallelism(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "parallelism values are currently not supported") {
|
||||
t.Fatalf("expected a parallelism error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -226,7 +221,7 @@ func TestRemote_planWithPlan(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
|
||||
op.PlanFile = &planfile.Reader{}
|
||||
|
@ -238,6 +233,7 @@ func TestRemote_planWithPlan(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
@ -245,7 +241,7 @@ func TestRemote_planWithPlan(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "saved plan is currently not supported") {
|
||||
t.Fatalf("expected a saved plan error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -255,7 +251,7 @@ func TestRemote_planWithPath(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
|
||||
op.PlanOutPath = "./testdata/plan"
|
||||
|
@ -267,6 +263,7 @@ func TestRemote_planWithPath(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
@ -274,7 +271,7 @@ func TestRemote_planWithPath(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "generated plan is currently not supported") {
|
||||
t.Fatalf("expected a generated plan error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -284,7 +281,7 @@ func TestRemote_planWithoutRefresh(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
|
||||
op.PlanRefresh = false
|
||||
|
@ -296,11 +293,12 @@ func TestRemote_planWithoutRefresh(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "refresh is currently not supported") {
|
||||
t.Fatalf("expected a refresh error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -333,8 +331,9 @@ func TestRemote_planWithTarget(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
addr, _ := addrs.ParseAbsResourceStr("null_resource.foo")
|
||||
|
||||
|
@ -382,7 +381,7 @@ func TestRemote_planWithTargetIncompatibleAPIVersion(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
|
||||
// Set the tfe client's RemoteAPIVersion to an empty string, to mimic
|
||||
|
@ -400,6 +399,7 @@ func TestRemote_planWithTargetIncompatibleAPIVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
@ -407,7 +407,7 @@ func TestRemote_planWithTargetIncompatibleAPIVersion(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "Resource targeting is not supported") {
|
||||
t.Fatalf("expected a targeting error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -417,7 +417,7 @@ func TestRemote_planWithVariables(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan-variables")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan-variables")
|
||||
defer configCleanup()
|
||||
|
||||
op.Variables = testVariables(terraform.ValueFromCLIArg, "foo", "bar")
|
||||
|
@ -429,11 +429,12 @@ func TestRemote_planWithVariables(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "variables are currently not supported") {
|
||||
t.Fatalf("expected a variables error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -443,7 +444,7 @@ func TestRemote_planNoConfig(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/empty")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/empty")
|
||||
defer configCleanup()
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
@ -454,6 +455,7 @@ func TestRemote_planNoConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
output := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
@ -461,7 +463,7 @@ func TestRemote_planNoConfig(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := output.Stderr()
|
||||
if !strings.Contains(errOutput, "configuration files found") {
|
||||
t.Fatalf("expected configuration files error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -471,8 +473,9 @@ func TestRemote_planNoChanges(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan-no-changes")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan-no-changes")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -509,8 +512,9 @@ func TestRemote_planForceLocal(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -544,8 +548,9 @@ func TestRemote_planWithoutOperationsEntitlement(t *testing.T) {
|
|||
b, bCleanup := testBackendNoOperations(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -593,8 +598,9 @@ func TestRemote_planWorkspaceWithoutOperations(t *testing.T) {
|
|||
t.Fatalf("error creating named workspace: %v", err)
|
||||
}
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = "no-operations"
|
||||
|
||||
|
@ -651,8 +657,9 @@ func TestRemote_planLockTimeout(t *testing.T) {
|
|||
t.Fatalf("error creating pending run: %v", err)
|
||||
}
|
||||
|
||||
op, configCleanup := testOperationPlanWithTimeout(t, "./testdata/plan", 50)
|
||||
op, configCleanup, done := testOperationPlanWithTimeout(t, "./testdata/plan", 50)
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
input := testInput(t, map[string]string{
|
||||
"cancel": "yes",
|
||||
|
@ -698,8 +705,9 @@ func TestRemote_planDestroy(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Destroy = true
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
@ -722,8 +730,9 @@ func TestRemote_planDestroyNoConfig(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/empty")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/empty")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Destroy = true
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
@ -756,8 +765,9 @@ func TestRemote_planWithWorkingDirectory(t *testing.T) {
|
|||
t.Fatalf("error configuring working directory: %v", err)
|
||||
}
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan-with-working-directory/terraform")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan-with-working-directory/terraform")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -814,8 +824,9 @@ func TestRemote_planWithWorkingDirectoryFromCurrentPath(t *testing.T) {
|
|||
|
||||
// For this test we need to give our current directory instead of the
|
||||
// full path to the configuration as we already changed directories.
|
||||
op, configCleanup := testOperationPlan(t, ".")
|
||||
op, configCleanup, done := testOperationPlan(t, ".")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -845,8 +856,9 @@ func TestRemote_planCostEstimation(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan-cost-estimation")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan-cost-estimation")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -879,8 +891,9 @@ func TestRemote_planPolicyPass(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan-policy-passed")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan-policy-passed")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -913,7 +926,7 @@ func TestRemote_planPolicyHardFail(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan-policy-hard-failed")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan-policy-hard-failed")
|
||||
defer configCleanup()
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
@ -924,6 +937,7 @@ func TestRemote_planPolicyHardFail(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
viewOutput := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
@ -931,7 +945,7 @@ func TestRemote_planPolicyHardFail(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := viewOutput.Stderr()
|
||||
if !strings.Contains(errOutput, "hard failed") {
|
||||
t.Fatalf("expected a policy check error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -952,7 +966,7 @@ func TestRemote_planPolicySoftFail(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup, playback := testOperationPlanWithDiagnostics(t, "./testdata/plan-policy-soft-failed")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan-policy-soft-failed")
|
||||
defer configCleanup()
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
@ -963,6 +977,7 @@ func TestRemote_planPolicySoftFail(t *testing.T) {
|
|||
}
|
||||
|
||||
<-run.Done()
|
||||
viewOutput := done(t)
|
||||
if run.Result == backend.OperationSuccess {
|
||||
t.Fatal("expected plan operation to fail")
|
||||
}
|
||||
|
@ -970,7 +985,7 @@ func TestRemote_planPolicySoftFail(t *testing.T) {
|
|||
t.Fatalf("expected plan to be empty")
|
||||
}
|
||||
|
||||
errOutput := playback().Err().Error()
|
||||
errOutput := viewOutput.Stderr()
|
||||
if !strings.Contains(errOutput, "soft failed") {
|
||||
t.Fatalf("expected a policy check error, got: %v", errOutput)
|
||||
}
|
||||
|
@ -991,8 +1006,9 @@ func TestRemote_planWithRemoteError(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan-with-error")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan-with-error")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = backend.DefaultStateName
|
||||
|
||||
|
@ -1022,8 +1038,9 @@ func TestRemote_planOtherError(t *testing.T) {
|
|||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
op, configCleanup := testOperationPlan(t, "./testdata/plan")
|
||||
op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
|
||||
defer configCleanup()
|
||||
defer done(t)
|
||||
|
||||
op.Workspace = "network-error" // custom error response in backend_mock.go
|
||||
|
||||
|
|
|
@ -297,43 +297,3 @@ func testVariables(s terraform.ValueSourceType, vs ...string) map[string]backend
|
|||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
// testRecordDiagnostics allows tests to record and later inspect diagnostics
|
||||
// emitted during an Operation. It returns a record function which can be set
|
||||
// as the ShowDiagnostics value of an Operation, and a playback function which
|
||||
// returns the recorded diagnostics for inspection.
|
||||
func testRecordDiagnostics(t *testing.T) (record func(vals ...interface{}), playback func() tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
record = func(vals ...interface{}) {
|
||||
diags = diags.Append(vals...)
|
||||
}
|
||||
playback = func() tfdiags.Diagnostics {
|
||||
diags.Sort()
|
||||
return diags
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// testLogDiagnostics returns a function which can be used as the
|
||||
// ShowDiagnostics value for an Operation, in order to help debugging during
|
||||
// tests. Any calls to this function result in test logs.
|
||||
func testLogDiagnostics(t *testing.T) func(vals ...interface{}) {
|
||||
return func(vals ...interface{}) {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(vals...)
|
||||
diags.Sort()
|
||||
|
||||
for _, diag := range diags {
|
||||
// NOTE: Since the caller here is not directly the TestLocal
|
||||
// function, t.Helper doesn't apply and so the log source
|
||||
// isn't correctly shown in the test log output. This seems
|
||||
// unavoidable as long as this is happening so indirectly.
|
||||
desc := diag.Description()
|
||||
if desc.Detail != "" {
|
||||
t.Logf("%s: %s", desc.Summary, desc.Detail)
|
||||
} else {
|
||||
t.Log(desc.Summary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -252,17 +252,6 @@ func (c *ApplyCommand) OperationRequest(
|
|||
opReq.Type = backend.OperationTypeApply
|
||||
opReq.View = view.Operation()
|
||||
|
||||
// FIXME: To allow errors to be easily rendered, the showDiagnostics method
|
||||
// accepts ...interface{}. The backend no longer needs this, as only
|
||||
// tfdiags.Diagnostics values are used. Once we have migrated plan and refresh
|
||||
// to use views, we can remove ShowDiagnostics and update the backend code to
|
||||
// call view.Diagnostics() instead.
|
||||
opReq.ShowDiagnostics = func(vals ...interface{}) {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(vals...)
|
||||
view.Diagnostics(diags)
|
||||
}
|
||||
|
||||
var err error
|
||||
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||
if err != nil {
|
||||
|
|
|
@ -147,12 +147,6 @@ func (c *PlanCommand) OperationRequest(
|
|||
opReq.Targets = args.Targets
|
||||
opReq.Type = backend.OperationTypePlan
|
||||
opReq.View = view.Operation()
|
||||
// FIXME: this shim is needed until the remote backend is migrated to views
|
||||
opReq.ShowDiagnostics = func(vals ...interface{}) {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(vals...)
|
||||
view.Diagnostics(diags)
|
||||
}
|
||||
|
||||
var err error
|
||||
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||
|
|
|
@ -135,12 +135,6 @@ func (c *RefreshCommand) OperationRequest(be backend.Enhanced, view views.Refres
|
|||
opReq.Targets = args.Targets
|
||||
opReq.Type = backend.OperationTypeRefresh
|
||||
opReq.View = view.Operation()
|
||||
// FIXME: this shim is needed until the remote backend is migrated to views
|
||||
opReq.ShowDiagnostics = func(vals ...interface{}) {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(vals...)
|
||||
view.Diagnostics(diags)
|
||||
}
|
||||
|
||||
var err error
|
||||
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||
|
|
|
@ -15,8 +15,6 @@ type View struct {
|
|||
streams *terminal.Streams
|
||||
colorize *colorstring.Colorize
|
||||
|
||||
// NOTE: compactWarnings is currently always false. When implementing
|
||||
// views for commands which support this flag, we will need to address this.
|
||||
compactWarnings bool
|
||||
|
||||
// This unfortunate wart is required to enable rendering of diagnostics which
|
||||
|
|
Loading…
Reference in New Issue