Merge pull request #27787 from hashicorp/alisdair/command-views-state-locker

clistate: Update clistate.Locker for command views
This commit is contained in:
Alisdair McDiarmid 2021-02-16 17:37:18 -05:00 committed by GitHub
commit 6375c6ce6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 641 additions and 245 deletions

View File

@ -10,7 +10,6 @@ import (
"io/ioutil"
"log"
"os"
"time"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/clistate"
@ -216,17 +215,13 @@ type Operation struct {
// ShowDiagnostics prints diagnostic messages to the UI.
ShowDiagnostics func(vals ...interface{})
// If LockState is true, the Operation must Lock any
// statemgr.Lockers for its duration, and Unlock when complete.
LockState bool
// StateLocker is used to lock the state while providing UI feedback to the
// user. This will be supplied by the Backend itself.
// user. This will be replaced by the Backend to update the context.
//
// If state locking is not necessary, this should be set to a no-op
// implementation of clistate.Locker.
StateLocker clistate.Locker
// The duration to retry obtaining a State lock.
StateLockTimeout time.Duration
// Workspace is the name of the workspace that this operation should run
// in, which controls which named state is used.
Workspace string

View File

@ -12,7 +12,6 @@ import (
"sync"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/states/statemgr"
@ -326,11 +325,7 @@ func (b *Local) Operation(ctx context.Context, op *backend.Operation) (*backend.
cancelCtx, cancel := context.WithCancel(context.Background())
runningOp.Cancel = cancel
if op.LockState {
op.StateLocker = clistate.NewLocker(stopCtx, op.StateLockTimeout, b.CLI, b.Colorize())
} else {
op.StateLocker = clistate.NewNoopLocker()
}
op.StateLocker = op.StateLocker.WithContext(stopCtx)
// Do it
go func() {

View File

@ -52,9 +52,9 @@ func (b *Local) opApply(
// the state was locked during succesfull context creation; unlock the state
// when the operation completes
defer func() {
err := op.StateLocker.Unlock(nil)
if err != nil {
op.ShowDiagnostics(err)
diags := op.StateLocker.Unlock()
if diags.HasErrors() {
op.ShowDiagnostics(diags)
runningOp.Result = backend.OperationFailure
}
}()

View File

@ -13,6 +13,7 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/providers"
@ -293,6 +294,7 @@ func testOperationApply(t *testing.T, configDir string) (*backend.Operation, fun
ConfigDir: configDir,
ConfigLoader: configLoader,
ShowDiagnostics: testLogDiagnostics(t),
StateLocker: clistate.NewNoopLocker(),
}, configCleanup
}

View File

@ -8,7 +8,6 @@ import (
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/configs/configload"
"github.com/hashicorp/terraform/plans/planfile"
@ -24,11 +23,7 @@ func (b *Local) Context(op *backend.Operation) (*terraform.Context, statemgr.Ful
// to ask for input/validate.
op.Type = backend.OperationTypeInvalid
if op.LockState {
op.StateLocker = clistate.NewLocker(context.Background(), op.StateLockTimeout, b.CLI, b.Colorize())
} else {
op.StateLocker = clistate.NewNoopLocker()
}
op.StateLocker = op.StateLocker.WithContext(context.Background())
ctx, _, stateMgr, diags := b.context(op)
return ctx, stateMgr, diags
@ -45,8 +40,7 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, *configload.
return nil, nil, nil, diags
}
log.Printf("[TRACE] backend/local: requesting state lock for workspace %q", op.Workspace)
if err := op.StateLocker.Lock(s, op.Type.String()); err != nil {
diags = diags.Append(errwrap.Wrapf("Error locking state: {{err}}", err))
if diags := op.StateLocker.Lock(s, op.Type.String()); diags.HasErrors() {
return nil, nil, nil, diags
}
@ -54,10 +48,7 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, *configload.
// If we're returning with errors, and thus not producing a valid
// context, we'll want to avoid leaving the workspace locked.
if diags.HasErrors() {
err := op.StateLocker.Unlock(nil)
if err != nil {
diags = diags.Append(errwrap.Wrapf("Error unlocking state: {{err}}", err))
}
diags = diags.Append(op.StateLocker.Unlock())
}
}()

View File

@ -4,7 +4,11 @@ import (
"testing"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/terminal"
)
func TestLocalContext(t *testing.T) {
@ -15,11 +19,15 @@ func TestLocalContext(t *testing.T) {
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir)
defer configCleanup()
streams, _ := terminal.StreamsForTesting(t)
view := views.NewView(streams)
stateLocker := clistate.NewLocker(0, views.NewStateLocker(arguments.ViewHuman, view))
op := &backend.Operation{
ConfigDir: configDir,
ConfigLoader: configLoader,
Workspace: backend.DefaultStateName,
LockState: true,
StateLocker: stateLocker,
}
_, _, diags := b.Context(op)
@ -39,11 +47,15 @@ func TestLocalContext_error(t *testing.T) {
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir)
defer configCleanup()
streams, _ := terminal.StreamsForTesting(t)
view := views.NewView(streams)
stateLocker := clistate.NewLocker(0, views.NewStateLocker(arguments.ViewHuman, view))
op := &backend.Operation{
ConfigDir: configDir,
ConfigLoader: configLoader,
Workspace: backend.DefaultStateName,
LockState: true,
StateLocker: stateLocker,
}
_, _, diags := b.Context(op)
@ -53,5 +65,4 @@ func TestLocalContext_error(t *testing.T) {
// Context() unlocks the state on failure
assertBackendStateUnlocked(t, b)
}

View File

@ -73,9 +73,9 @@ func (b *Local) opPlan(
// the state was locked during succesfull context creation; unlock the state
// when the operation completes
defer func() {
err := op.StateLocker.Unlock(nil)
if err != nil {
op.ShowDiagnostics(err)
diags := op.StateLocker.Unlock()
if diags.HasErrors() {
op.ShowDiagnostics(diags)
runningOp.Result = backend.OperationFailure
}
}()

View File

@ -9,6 +9,7 @@ import (
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/plans"
@ -740,6 +741,7 @@ func testOperationPlan(t *testing.T, configDir string) (*backend.Operation, func
ConfigDir: configDir,
ConfigLoader: configLoader,
ShowDiagnostics: testLogDiagnostics(t),
StateLocker: clistate.NewNoopLocker(),
}, configCleanup
}

View File

@ -55,9 +55,9 @@ func (b *Local) opRefresh(
// the state was locked during succesfull context creation; unlock the state
// when the operation completes
defer func() {
err := op.StateLocker.Unlock(nil)
if err != nil {
op.ShowDiagnostics(err)
diags := op.StateLocker.Unlock()
if diags.HasErrors() {
op.ShowDiagnostics(diags)
runningOp.Result = backend.OperationFailure
}
}()

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/providers"
@ -260,8 +261,8 @@ func testOperationRefresh(t *testing.T, configDir string) (*backend.Operation, f
Type: backend.OperationTypeRefresh,
ConfigDir: configDir,
ConfigLoader: configLoader,
LockState: true,
ShowDiagnostics: testLogDiagnostics(t),
StateLocker: clistate.NewNoopLocker(),
}, configCleanup
}

View File

@ -14,7 +14,11 @@ import (
version "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/plans/planfile"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/hashicorp/terraform/terraform"
@ -26,14 +30,24 @@ import (
func testOperationApply(t *testing.T, configDir string) (*backend.Operation, func()) {
t.Helper()
return testOperationApplyWithTimeout(t, configDir, 0)
}
func testOperationApplyWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backend.Operation, func()) {
t.Helper()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir)
streams, _ := terminal.StreamsForTesting(t)
view := views.NewStateLocker(arguments.ViewHuman, views.NewView(streams))
return &backend.Operation{
ConfigDir: configDir,
ConfigLoader: configLoader,
Parallelism: defaultParallelism,
PlanRefresh: true,
ShowDiagnostics: testLogDiagnostics(t),
StateLocker: clistate.NewLocker(timeout, view),
Type: backend.OperationTypeApply,
}, configCleanup
}
@ -878,7 +892,7 @@ func TestRemote_applyLockTimeout(t *testing.T) {
t.Fatalf("error creating pending run: %v", err)
}
op, configCleanup := testOperationApply(t, "./testdata/apply")
op, configCleanup := testOperationApplyWithTimeout(t, "./testdata/apply", 50*time.Millisecond)
defer configCleanup()
input := testInput(t, map[string]string{
@ -886,7 +900,6 @@ func TestRemote_applyLockTimeout(t *testing.T) {
"approve": "yes",
})
op.StateLockTimeout = 50 * time.Millisecond
op.UIIn = input
op.UIOut = b.CLI
op.Workspace = backend.DefaultStateName

View File

@ -11,7 +11,6 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/hashicorp/terraform/terraform"
@ -23,11 +22,7 @@ import (
func (b *Remote) Context(op *backend.Operation) (*terraform.Context, statemgr.Full, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
if op.LockState {
op.StateLocker = clistate.NewLocker(context.Background(), op.StateLockTimeout, b.CLI, b.cliColorize())
} else {
op.StateLocker = clistate.NewNoopLocker()
}
op.StateLocker = op.StateLocker.WithContext(context.Background())
// Get the remote workspace name.
remoteWorkspaceName := b.getRemoteWorkspaceName(op.Workspace)
@ -41,8 +36,7 @@ func (b *Remote) Context(op *backend.Operation) (*terraform.Context, statemgr.Fu
}
log.Printf("[TRACE] backend/remote: requesting state lock for workspace %q", remoteWorkspaceName)
if err := op.StateLocker.Lock(stateMgr, op.Type.String()); err != nil {
diags = diags.Append(errwrap.Wrapf("Error locking state: {{err}}", err))
if diags := op.StateLocker.Lock(stateMgr, op.Type.String()); diags.HasErrors() {
return nil, nil, diags
}
@ -50,10 +44,7 @@ func (b *Remote) Context(op *backend.Operation) (*terraform.Context, statemgr.Fu
// If we're returning with errors, and thus not producing a valid
// context, we'll want to avoid leaving the remote workspace locked.
if diags.HasErrors() {
err := op.StateLocker.Unlock(nil)
if err != nil {
diags = diags.Append(errwrap.Wrapf("Error unlocking state: {{err}}", err))
}
diags = diags.Append(op.StateLocker.Unlock())
}
}()

View File

@ -6,8 +6,12 @@ import (
tfe "github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/zclconf/go-cty/cty"
)
@ -183,11 +187,14 @@ func TestRemoteContextWithVars(t *testing.T) {
t.Fatal(err)
}
streams, _ := terminal.StreamsForTesting(t)
view := views.NewStateLocker(arguments.ViewHuman, views.NewView(streams))
op := &backend.Operation{
ConfigDir: configDir,
ConfigLoader: configLoader,
StateLocker: clistate.NewLocker(0, view),
Workspace: backend.DefaultStateName,
LockState: true,
}
v := test.Opts

View File

@ -260,15 +260,16 @@ in order to capture the filesystem context the remote workspace expects:
return r, generalError("Failed to create run", err)
}
// When the lock timeout is set,
if op.StateLockTimeout > 0 {
// When the lock timeout is set, if the run is still pending and
// cancellable after that period, we attempt to cancel it.
if lockTimeout := op.StateLocker.Timeout(); lockTimeout > 0 {
go func() {
select {
case <-stopCtx.Done():
return
case <-cancelCtx.Done():
return
case <-time.After(op.StateLockTimeout):
case <-time.After(lockTimeout):
// Retrieve the run to get its current status.
r, err := b.client.Runs.Read(cancelCtx, r.ID)
if err != nil {

View File

@ -13,7 +13,11 @@ import (
tfe "github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/plans/planfile"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/hashicorp/terraform/terraform"
@ -24,14 +28,24 @@ import (
func testOperationPlan(t *testing.T, configDir string) (*backend.Operation, func()) {
t.Helper()
return testOperationPlanWithTimeout(t, configDir, 0)
}
func testOperationPlanWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backend.Operation, func()) {
t.Helper()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir)
streams, _ := terminal.StreamsForTesting(t)
view := views.NewStateLocker(arguments.ViewHuman, views.NewView(streams))
return &backend.Operation{
ConfigDir: configDir,
ConfigLoader: configLoader,
Parallelism: defaultParallelism,
PlanRefresh: true,
ShowDiagnostics: testLogDiagnostics(t),
StateLocker: clistate.NewLocker(timeout, view),
Type: backend.OperationTypePlan,
}, configCleanup
}
@ -625,7 +639,7 @@ func TestRemote_planLockTimeout(t *testing.T) {
t.Fatalf("error creating pending run: %v", err)
}
op, configCleanup := testOperationPlan(t, "./testdata/plan")
op, configCleanup := testOperationPlanWithTimeout(t, "./testdata/plan", 50)
defer configCleanup()
input := testInput(t, map[string]string{
@ -633,7 +647,6 @@ func TestRemote_planLockTimeout(t *testing.T) {
"approve": "yes",
})
op.StateLockTimeout = 50 * time.Millisecond
op.UIIn = input
op.UIOut = b.CLI
op.Workspace = backend.DefaultStateName

View File

@ -940,6 +940,7 @@ func TestApply_planNoModuleFiles(t *testing.T) {
p := applyFixtureProvider()
planPath := applyFixturePlanFile(t)
view, _ := testView(t)
view, _ := testView(t)
apply := &ApplyCommand{

View File

@ -7,33 +7,25 @@ package clistate
import (
"context"
"fmt"
"strings"
"sync"
"time"
"github.com/hashicorp/errwrap"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/internal/helper/slowmessage"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/mitchellh/cli"
"github.com/mitchellh/colorstring"
"github.com/hashicorp/terraform/tfdiags"
)
const (
LockThreshold = 400 * time.Millisecond
LockMessage = "Acquiring state lock. This may take a few moments..."
LockErrorMessage = `Error acquiring the state lock: {{err}}
LockErrorMessage = `Error message: %s
Terraform acquires a state lock to protect the state from being written
by multiple users at the same time. Please resolve the issue above and try
again. For most commands, you can disable locking with the "-lock=false"
flag, but this is not recommended.`
UnlockMessage = "Releasing state lock. This may take a few moments..."
UnlockErrorMessage = `
[reset][bold][red]Error releasing the state lock![reset][red]
Error message: %s
UnlockErrorMessage = `Error message: %s
Terraform acquires a lock when accessing your state to prevent others
running Terraform to potentially modify the state at the same time. An
@ -46,8 +38,7 @@ In this scenario, please call the "force-unlock" command to unlock the
state manually. This is a very dangerous operation since if it is done
erroneously it could result in two people modifying state at the same time.
Only call this command if you're certain that the unlock above failed and
that no one else is holding a lock.
`
that no one else is holding a lock.`
)
// Locker allows for more convenient usage of the lower-level statemgr.Locker
@ -60,12 +51,17 @@ that no one else is holding a lock.
// Unlock, which is at a minimum the LockID string returned by the
// statemgr.Locker.
type Locker interface {
// Returns a shallow copy of the locker with its context changed to ctx.
WithContext(ctx context.Context) Locker
// Lock the provided state manager, storing the reason string in the LockInfo.
Lock(s statemgr.Locker, reason string) error
Lock(s statemgr.Locker, reason string) tfdiags.Diagnostics
// Unlock the previously locked state.
// An optional error can be passed in, and will be combined with any error
// from the Unlock operation.
Unlock(error) error
Unlock() tfdiags.Diagnostics
// Timeout returns the configured timeout duration
Timeout() time.Duration
}
type locker struct {
@ -73,34 +69,43 @@ type locker struct {
ctx context.Context
timeout time.Duration
state statemgr.Locker
ui cli.Ui
color *colorstring.Colorize
view views.StateLocker
lockID string
}
var _ Locker = (*locker)(nil)
// Create a new Locker.
// This Locker uses state.LockWithContext to retry the lock until the provided
// timeout is reached, or the context is canceled. Lock progress will be be
// reported to the user through the provided UI.
func NewLocker(
ctx context.Context,
timeout time.Duration,
ui cli.Ui,
color *colorstring.Colorize) Locker {
l := &locker{
ctx: ctx,
func NewLocker(timeout time.Duration, view views.StateLocker) Locker {
return &locker{
ctx: context.Background(),
timeout: timeout,
ui: ui,
color: color,
view: view,
}
}
// WithContext returns a new Locker with the specified context, copying the
// timeout and view parameters from the original Locker.
func (l *locker) WithContext(ctx context.Context) Locker {
if ctx == nil {
panic("nil context")
}
return &locker{
ctx: ctx,
timeout: l.timeout,
view: l.view,
}
return l
}
// Locker locks the given state and outputs to the user if locking is taking
// longer than the threshold. The lock is retried until the context is
// cancelled.
func (l *locker) Lock(s statemgr.Locker, reason string) error {
func (l *locker) Lock(s statemgr.Locker, reason string) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
l.mu.Lock()
defer l.mu.Unlock()
@ -116,46 +121,49 @@ func (l *locker) Lock(s statemgr.Locker, reason string) error {
id, err := statemgr.LockWithContext(ctx, s, lockInfo)
l.lockID = id
return err
}, func() {
if l.ui != nil {
l.ui.Output(l.color.Color(LockMessage))
}
})
}, l.view.Locking)
if err != nil {
return errwrap.Wrapf(strings.TrimSpace(LockErrorMessage), err)
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Error acquiring the state lock",
fmt.Sprintf(LockErrorMessage, err),
))
}
return nil
return diags
}
func (l *locker) Unlock(parentErr error) error {
func (l *locker) Unlock() tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
l.mu.Lock()
defer l.mu.Unlock()
if l.lockID == "" {
return parentErr
return diags
}
err := slowmessage.Do(LockThreshold, func() error {
return l.state.Unlock(l.lockID)
}, func() {
if l.ui != nil {
l.ui.Output(l.color.Color(UnlockMessage))
}
})
}, l.view.Unlocking)
if err != nil {
l.ui.Output(l.color.Color(fmt.Sprintf(
"\n"+strings.TrimSpace(UnlockErrorMessage)+"\n", err)))
parentErr = multierror.Append(parentErr, err)
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Error releasing the state lock",
fmt.Sprintf(UnlockErrorMessage, err),
))
}
return parentErr
return diags
}
func (l *locker) Timeout() time.Duration {
return l.timeout
}
type noopLocker struct{}
// NewNoopLocker returns a valid Locker that does nothing.
@ -163,10 +171,20 @@ func NewNoopLocker() Locker {
return noopLocker{}
}
func (l noopLocker) Lock(statemgr.Locker, string) error {
var _ Locker = noopLocker{}
func (l noopLocker) WithContext(ctx context.Context) Locker {
return l
}
func (l noopLocker) Lock(statemgr.Locker, string) tfdiags.Diagnostics {
return nil
}
func (l noopLocker) Unlock(err error) error {
return err
func (l noopLocker) Unlock() tfdiags.Diagnostics {
return nil
}
func (l noopLocker) Timeout() time.Duration {
return 0
}

View File

@ -1,23 +1,24 @@
package clistate
import (
"context"
"testing"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/mitchellh/cli"
"github.com/mitchellh/colorstring"
)
func TestUnlock(t *testing.T) {
ui := new(cli.MockUi)
streams, _ := terminal.StreamsForTesting(t)
view := views.NewView(streams)
l := NewLocker(context.Background(), 0, ui, &colorstring.Colorize{Disable: true})
l := NewLocker(0, views.NewStateLocker(arguments.ViewHuman, view))
l.Lock(statemgr.NewUnlockErrorFull(nil, nil), "test-lock")
err := l.Unlock(nil)
if err != nil {
t.Log(err.Error())
diags := l.Unlock()
if diags.HasErrors() {
t.Log(diags.Err().Error())
} else {
t.Error("expected error")
}

View File

@ -104,9 +104,9 @@ func (c *ConsoleCommand) Run(args []string) int {
// Successfully creating the context can result in a lock, so ensure we release it
defer func() {
err := opReq.StateLocker.Unlock(nil)
if err != nil {
c.Ui.Error(err.Error())
diags := opReq.StateLocker.Unlock()
if diags.HasErrors() {
c.showDiagnostics(diags)
}
}()

View File

@ -27,10 +27,12 @@ func TestConsole_basic(t *testing.T) {
p := testProvider()
ui := cli.NewMockUi()
view, _ := testView(t)
c := &ConsoleCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -76,10 +78,12 @@ func TestConsole_tfvars(t *testing.T) {
},
}
ui := cli.NewMockUi()
view, _ := testView(t)
c := &ConsoleCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -127,10 +131,12 @@ func TestConsole_unsetRequiredVars(t *testing.T) {
},
}
ui := cli.NewMockUi()
view, _ := testView(t)
c := &ConsoleCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -159,10 +165,12 @@ func TestConsole_variables(t *testing.T) {
p := testProvider()
ui := cli.NewMockUi()
view, _ := testView(t)
c := &ConsoleCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -200,11 +208,13 @@ func TestConsole_modules(t *testing.T) {
p := applyFixtureProvider()
ui := cli.NewMockUi()
view, _ := testView(t)
c := &ConsoleCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}

View File

@ -218,9 +218,9 @@ func (c *ImportCommand) Run(args []string) int {
// Successfully creating the context can result in a lock, so ensure we release it
defer func() {
err := opReq.StateLocker.Unlock(nil)
if err != nil {
c.Ui.Error(err.Error())
diags := opReq.StateLocker.Unlock()
if diags.HasErrors() {
c.showDiagnostics(diags)
}
}()

View File

@ -35,10 +35,12 @@ func TestInit_empty(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -56,10 +58,12 @@ func TestInit_multipleArgs(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -80,10 +84,12 @@ func TestInit_fromModule_cwdDest(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -130,10 +136,12 @@ func TestInit_fromModule_dstInSrc(t *testing.T) {
}
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -157,10 +165,12 @@ func TestInit_get(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -184,10 +194,12 @@ func TestInit_getUpgradeModules(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -214,10 +226,12 @@ func TestInit_backend(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -242,10 +256,12 @@ func TestInit_backendUnset(t *testing.T) {
log.Printf("[TRACE] TestInit_backendUnset: beginning first init")
ui := cli.NewMockUi()
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -272,10 +288,12 @@ func TestInit_backendUnset(t *testing.T) {
}
ui := cli.NewMockUi()
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -303,10 +321,12 @@ func TestInit_backendConfigFile(t *testing.T) {
t.Run("good-config-file", func(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
args := []string{"-backend-config", "input.config"}
@ -324,10 +344,12 @@ func TestInit_backendConfigFile(t *testing.T) {
// the backend config file must not be a full terraform block
t.Run("full-backend-config-file", func(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
args := []string{"-backend-config", "backend.config"}
@ -342,10 +364,12 @@ func TestInit_backendConfigFile(t *testing.T) {
// the backend config file must match the schema for the backend
t.Run("invalid-config-file", func(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
args := []string{"-backend-config", "invalid.config"}
@ -360,10 +384,12 @@ func TestInit_backendConfigFile(t *testing.T) {
// missing file is an error
t.Run("missing-config-file", func(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
args := []string{"-backend-config", "missing.config"}
@ -378,10 +404,12 @@ func TestInit_backendConfigFile(t *testing.T) {
// blank filename clears the backend config
t.Run("blank-config-file", func(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
args := []string{"-backend-config="}
@ -429,10 +457,12 @@ func TestInit_backendConfigFilePowershellConfusion(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -468,10 +498,12 @@ func TestInit_backendConfigFileChange(t *testing.T) {
})()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -495,10 +527,12 @@ func TestInit_backendConfigKV(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -522,10 +556,12 @@ func TestInit_backendConfigKVReInit(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -539,6 +575,7 @@ func TestInit_backendConfigKVReInit(t *testing.T) {
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -583,10 +620,12 @@ func TestInit_backendConfigKVReInitWithConfigDiff(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -600,6 +639,7 @@ func TestInit_backendConfigKVReInitWithConfigDiff(t *testing.T) {
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -629,10 +669,12 @@ func TestInit_backendCli_no_config_block(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -667,10 +709,12 @@ func TestInit_backendReinitWithExtra(t *testing.T) {
}
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -710,10 +754,12 @@ func TestInit_backendReinitConfigToExtra(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -741,6 +787,7 @@ func TestInit_backendReinitConfigToExtra(t *testing.T) {
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -766,10 +813,12 @@ func TestInit_inputFalse(t *testing.T) {
defer testChdir(t, td)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -805,6 +854,7 @@ func TestInit_inputFalse(t *testing.T) {
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -823,6 +873,7 @@ func TestInit_inputFalse(t *testing.T) {
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -842,6 +893,7 @@ func TestInit_getProvider(t *testing.T) {
overrides := metaOverridesForProvider(testProvider())
ui := new(cli.MockUi)
view, _ := testView(t)
providerSource, close := newMockProviderSource(t, map[string][]string{
// looking for an exact version
"exact": {"1.2.3"},
@ -854,6 +906,7 @@ func TestInit_getProvider(t *testing.T) {
m := Meta{
testingOverrides: overrides,
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -919,7 +972,9 @@ func TestInit_getProvider(t *testing.T) {
}
ui := new(cli.MockUi)
view, _ := testView(t)
m.Ui = ui
m.View = view
c := &InitCommand{
Meta: m,
}
@ -944,6 +999,7 @@ func TestInit_getProviderSource(t *testing.T) {
overrides := metaOverridesForProvider(testProvider())
ui := new(cli.MockUi)
view, _ := testView(t)
providerSource, close := newMockProviderSource(t, map[string][]string{
// looking for an exact version
"acme/alpha": {"1.2.3"},
@ -955,6 +1011,7 @@ func TestInit_getProviderSource(t *testing.T) {
m := Meta{
testingOverrides: overrides,
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -993,6 +1050,7 @@ func TestInit_getProviderLegacyFromState(t *testing.T) {
overrides := metaOverridesForProvider(testProvider())
ui := new(cli.MockUi)
view, _ := testView(t)
providerSource, close := newMockProviderSource(t, map[string][]string{
"acme/alpha": {"1.2.3"},
})
@ -1000,6 +1058,7 @@ func TestInit_getProviderLegacyFromState(t *testing.T) {
m := Meta{
testingOverrides: overrides,
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -1033,6 +1092,7 @@ func TestInit_getProviderInvalidPackage(t *testing.T) {
overrides := metaOverridesForProvider(testProvider())
ui := new(cli.MockUi)
view, _ := testView(t)
// create a provider source which allows installing an invalid package
addr := addrs.MustParseProviderSourceString("invalid/package")
@ -1053,6 +1113,7 @@ func TestInit_getProviderInvalidPackage(t *testing.T) {
m := Meta{
testingOverrides: overrides,
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -1109,8 +1170,10 @@ func TestInit_getProviderDetectedLegacy(t *testing.T) {
}
ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{
Ui: ui,
View: view,
ProviderSource: multiSource,
}
@ -1166,9 +1229,11 @@ func TestInit_providerSource(t *testing.T) {
defer close()
ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -1277,9 +1342,11 @@ func TestInit_cancel(t *testing.T) {
close(shutdownCh)
ui := cli.NewMockUi()
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
ShutdownCh: shutdownCh,
}
@ -1320,9 +1387,11 @@ func TestInit_getUpgradePlugins(t *testing.T) {
defer close()
ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -1444,9 +1513,11 @@ func TestInit_getProviderMissing(t *testing.T) {
defer close()
ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -1472,10 +1543,12 @@ func TestInit_checkRequiredVersion(t *testing.T) {
defer testChdir(t, td)()
ui := cli.NewMockUi()
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -1505,9 +1578,11 @@ func TestInit_providerLockFile(t *testing.T) {
defer close()
ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -1555,10 +1630,12 @@ func TestInit_pluginDirReset(t *testing.T) {
defer close()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
},
}
@ -1591,6 +1668,7 @@ func TestInit_pluginDirReset(t *testing.T) {
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource, // still empty
},
}
@ -1623,9 +1701,11 @@ func TestInit_pluginDirProviders(t *testing.T) {
defer close()
ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -1723,9 +1803,11 @@ func TestInit_pluginDirProvidersDoesNotGet(t *testing.T) {
defer close()
ui := cli.NewMockUi()
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -1795,9 +1877,11 @@ func TestInit_pluginDirWithBuiltIn(t *testing.T) {
defer close()
ui := cli.NewMockUi()
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
}
@ -1832,9 +1916,11 @@ func TestInit_invalidBuiltInProviders(t *testing.T) {
defer close()
ui := cli.NewMockUi()
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
ProviderSource: providerSource,
}

View File

@ -18,7 +18,9 @@ import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/terraform/backend"
remoteBackend "github.com/hashicorp/terraform/backend/remote"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/states/statemgr"
@ -341,15 +343,20 @@ func (m *Meta) Operation(b backend.Backend) *backend.Operation {
panic(fmt.Sprintf("failed to encode backend configuration for plan: %s", err))
}
stateLocker := clistate.NewNoopLocker()
if m.stateLock {
view := views.NewStateLocker(arguments.ViewHuman, m.View)
stateLocker = clistate.NewLocker(m.stateLockTimeout, view)
}
return &backend.Operation{
PlanOutBackend: planOutBackend,
Parallelism: m.parallelism,
Targets: m.targets,
UIIn: m.UIInput(),
UIOut: m.Ui,
Workspace: workspace,
LockState: m.stateLock,
StateLockTimeout: m.stateLockTimeout,
PlanOutBackend: planOutBackend,
Parallelism: m.parallelism,
Targets: m.targets,
UIIn: m.UIInput(),
UIOut: m.Ui,
Workspace: workspace,
StateLocker: stateLocker,
}
}
@ -802,12 +809,13 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
}
if m.stateLock {
stateLocker := clistate.NewLocker(context.Background(), m.stateLockTimeout, m.Ui, m.Colorize())
view := views.NewStateLocker(arguments.ViewHuman, m.View)
stateLocker := clistate.NewLocker(m.stateLockTimeout, view)
if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil {
diags = diags.Append(fmt.Errorf("Error locking state: %s", err))
return nil, diags
}
defer stateLocker.Unlock(nil)
defer stateLocker.Unlock()
}
configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType())
@ -886,12 +894,13 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista
}
if m.stateLock {
stateLocker := clistate.NewLocker(context.Background(), m.stateLockTimeout, m.Ui, m.Colorize())
view := views.NewStateLocker(arguments.ViewHuman, m.View)
stateLocker := clistate.NewLocker(m.stateLockTimeout, view)
if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil {
diags = diags.Append(fmt.Errorf("Error locking state: %s", err))
return nil, diags
}
defer stateLocker.Unlock(nil)
defer stateLocker.Unlock()
}
configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType())

View File

@ -12,7 +12,9 @@ import (
"strings"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/hashicorp/terraform/terraform"
@ -325,17 +327,20 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
if m.stateLock {
lockCtx := context.Background()
lockerOne := clistate.NewLocker(lockCtx, m.stateLockTimeout, m.Ui, m.Colorize())
if err := lockerOne.Lock(stateOne, "migration source state"); err != nil {
return fmt.Errorf("Error locking source state: %s", err)
}
defer lockerOne.Unlock(nil)
view := views.NewStateLocker(arguments.ViewHuman, m.View)
locker := clistate.NewLocker(m.stateLockTimeout, view)
lockerTwo := clistate.NewLocker(lockCtx, m.stateLockTimeout, m.Ui, m.Colorize())
if err := lockerTwo.Lock(stateTwo, "migration destination state"); err != nil {
return fmt.Errorf("Error locking destination state: %s", err)
lockerOne := locker.WithContext(lockCtx)
if diags := lockerOne.Lock(stateOne, "migration source state"); diags.HasErrors() {
return diags.Err()
}
defer lockerTwo.Unlock(nil)
defer lockerOne.Unlock()
lockerTwo := locker.WithContext(lockCtx)
if diags := lockerTwo.Lock(stateTwo, "migration destination state"); diags.HasErrors() {
return diags.Err()
}
defer lockerTwo.Unlock()
// We now own a lock, so double check that we have the version
// corresponding to the lock.

View File

@ -1876,6 +1876,8 @@ func TestBackendFromState(t *testing.T) {
func testMetaBackend(t *testing.T, args []string) *Meta {
var m Meta
m.Ui = new(cli.MockUi)
view, _ := testView(t)
m.View = view
m.process(args)
f := m.extendedFlagSet("test")
if err := f.Parse(args); err != nil {

View File

@ -22,10 +22,12 @@ import (
func TestShow(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &ShowCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -46,10 +48,12 @@ func TestShow_noArgs(t *testing.T) {
defer testChdir(t, stateDir)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &ShowCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -93,10 +97,12 @@ func TestShow_aliasedProvider(t *testing.T) {
defer testChdir(t, stateDir)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &ShowCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -121,10 +127,12 @@ func TestShow_noArgsNoState(t *testing.T) {
defer testChdir(t, stateDir)()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &ShowCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -139,10 +147,12 @@ func TestShow_plan(t *testing.T) {
planPath := testPlanFileNoop(t)
ui := cli.NewMockUi()
view, _ := testView(t)
c := &ShowCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -164,10 +174,12 @@ func TestShow_planWithChanges(t *testing.T) {
planPathWithChanges := showFixturePlanFile(t, plans.DeleteThenCreate)
ui := cli.NewMockUi()
view, _ := testView(t)
c := &ShowCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(showFixtureProvider()),
Ui: ui,
View: view,
},
}
@ -190,10 +202,12 @@ func TestShow_plan_json(t *testing.T) {
planPath := showFixturePlanFile(t, plans.Create)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &ShowCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(showFixtureProvider()),
Ui: ui,
View: view,
},
}
@ -212,10 +226,12 @@ func TestShow_state(t *testing.T) {
defer os.RemoveAll(filepath.Dir(statePath))
ui := new(cli.MockUi)
view, _ := testView(t)
c := &ShowCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -354,9 +370,11 @@ func TestShow_json_output_state(t *testing.T) {
p := showFixtureProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
m := Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
ProviderSource: providerSource,
}

View File

@ -1,12 +1,13 @@
package command
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
"github.com/mitchellh/cli"
@ -49,12 +50,16 @@ func (c *StateMvCommand) Run(args []string) int {
}
if c.stateLock {
stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateFromMgr, "state-mv"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking source state: %s", err))
stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
if diags := stateLocker.Lock(stateFromMgr, "state-mv"); diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
defer stateLocker.Unlock(nil)
defer func() {
if diags := stateLocker.Unlock(); diags.HasErrors() {
c.showDiagnostics(diags)
}
}()
}
if err := stateFromMgr.RefreshState(); err != nil {
@ -83,12 +88,16 @@ func (c *StateMvCommand) Run(args []string) int {
}
if c.stateLock {
stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateToMgr, "state-mv"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking destination state: %s", err))
stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
if diags := stateLocker.Lock(stateToMgr, "state-mv"); diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
defer stateLocker.Unlock(nil)
defer func() {
if diags := stateLocker.Unlock(); diags.HasErrors() {
c.showDiagnostics(diags)
}
}()
}
if err := stateToMgr.RefreshState(); err != nil {

View File

@ -58,11 +58,13 @@ func TestStateMv(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -204,11 +206,13 @@ func TestStateMv_resourceToInstance(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -277,12 +281,14 @@ func TestStateMv_resourceToInstanceErr(t *testing.T) {
p := testProvider()
ui := cli.NewMockUi()
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -344,11 +350,13 @@ func TestStateMv_resourceToInstanceErrInAutomation(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
RunningInAutomation: true,
},
},
@ -416,11 +424,13 @@ func TestStateMv_instanceToResource(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -489,11 +499,13 @@ func TestStateMv_instanceToNewResource(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -560,11 +572,13 @@ func TestStateMv_differentResourceTypes(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -636,10 +650,12 @@ func TestStateMv_explicitWithBackend(t *testing.T) {
// init our backend
ui := new(cli.MockUi)
view, _ := testView(t)
ic := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}
@ -656,6 +672,7 @@ func TestStateMv_explicitWithBackend(t *testing.T) {
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -713,11 +730,13 @@ func TestStateMv_backupExplicit(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -762,11 +781,13 @@ func TestStateMv_stateOutNew(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -834,11 +855,13 @@ func TestStateMv_stateOutExisting(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -877,11 +900,13 @@ func TestStateMv_noState(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -945,11 +970,13 @@ func TestStateMv_stateOutNew_count(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -1019,11 +1046,13 @@ func TestStateMv_stateOutNew_largeCount(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -1089,11 +1118,13 @@ func TestStateMv_stateOutNew_nestedModule(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -1145,11 +1176,13 @@ func TestStateMv_toNewModule(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -1244,11 +1277,13 @@ func TestStateMv_withinBackend(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -1314,11 +1349,13 @@ func TestStateMv_fromBackendToLocal(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -1365,11 +1402,13 @@ func TestStateMv_onlyResourceInModule(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}

View File

@ -1,13 +1,14 @@
package command
import (
"context"
"fmt"
"io"
"os"
"strings"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/states/statefile"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/mitchellh/cli"
@ -93,12 +94,16 @@ func (c *StatePushCommand) Run(args []string) int {
}
if c.stateLock {
stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateMgr, "state-push"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
if diags := stateLocker.Lock(stateMgr, "state-push"); diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
defer stateLocker.Unlock(nil)
defer func() {
if diags := stateLocker.Unlock(); diags.HasErrors() {
c.showDiagnostics(diags)
}
}()
}
if err := stateMgr.RefreshState(); err != nil {

View File

@ -23,10 +23,12 @@ func TestStatePush_empty(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StatePushCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -50,10 +52,12 @@ func TestStatePush_lockedState(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StatePushCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -83,10 +87,12 @@ func TestStatePush_replaceMatch(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StatePushCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -119,10 +125,12 @@ func TestStatePush_replaceMatchStdin(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StatePushCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -148,10 +156,12 @@ func TestStatePush_lineageMismatch(t *testing.T) {
p := testProvider()
ui := cli.NewMockUi()
view, _ := testView(t)
c := &StatePushCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -177,10 +187,12 @@ func TestStatePush_serialNewer(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StatePushCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -206,10 +218,12 @@ func TestStatePush_serialOlder(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StatePushCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -236,8 +250,9 @@ func TestStatePush_forceRemoteState(t *testing.T) {
// init the backend
ui := new(cli.MockUi)
view, _ := testView(t)
initCmd := &InitCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
if code := initCmd.Run([]string{}); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
@ -246,7 +261,7 @@ func TestStatePush_forceRemoteState(t *testing.T) {
// create a new workspace
ui = new(cli.MockUi)
newCmd := &WorkspaceNewCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
if code := newCmd.Run([]string{"test"}); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
@ -268,7 +283,7 @@ func TestStatePush_forceRemoteState(t *testing.T) {
// push our local state to that new workspace
ui = new(cli.MockUi)
c := &StatePushCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
args := []string{"-force", statePath}

View File

@ -1,12 +1,13 @@
package command
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
"github.com/mitchellh/cli"
@ -74,12 +75,16 @@ func (c *StateReplaceProviderCommand) Run(args []string) int {
// Acquire lock if requested
if c.stateLock {
stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateMgr, "state-replace-provider"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking source state: %s", err))
stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
if diags := stateLocker.Lock(stateMgr, "state-replace-provider"); diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
defer stateLocker.Unlock(nil)
defer func() {
if diags := stateLocker.Unlock(); diags.HasErrors() {
c.showDiagnostics(diags)
}
}()
}
// Refresh and load state

View File

@ -65,10 +65,12 @@ func TestStateReplaceProvider(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateReplaceProviderCommand{
StateMeta{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
},
}
@ -99,10 +101,12 @@ func TestStateReplaceProvider(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateReplaceProviderCommand{
StateMeta{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
},
}
@ -133,10 +137,12 @@ func TestStateReplaceProvider(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateReplaceProviderCommand{
StateMeta{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
},
}
@ -166,10 +172,12 @@ func TestStateReplaceProvider(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateReplaceProviderCommand{
StateMeta{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
},
}
@ -193,10 +201,12 @@ func TestStateReplaceProvider(t *testing.T) {
t.Run("invalid flags", func(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateReplaceProviderCommand{
StateMeta{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
},
}
@ -217,10 +227,12 @@ func TestStateReplaceProvider(t *testing.T) {
t.Run("wrong number of arguments", func(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateReplaceProviderCommand{
StateMeta{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
},
}
@ -237,10 +249,12 @@ func TestStateReplaceProvider(t *testing.T) {
t.Run("invalid provider strings", func(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateReplaceProviderCommand{
StateMeta{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
},
}

View File

@ -1,12 +1,13 @@
package command
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/tfdiags"
"github.com/mitchellh/cli"
)
@ -44,12 +45,16 @@ func (c *StateRmCommand) Run(args []string) int {
}
if c.stateLock {
stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateMgr, "state-rm"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
if diags := stateLocker.Lock(stateMgr, "state-rm"); diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
defer stateLocker.Unlock(nil)
defer func() {
if diags := stateLocker.Unlock(); diags.HasErrors() {
c.showDiagnostics(diags)
}
}()
}
if err := stateMgr.RefreshState(); err != nil {

View File

@ -49,11 +49,13 @@ func TestStateRm(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -117,11 +119,13 @@ func TestStateRmNotChildModule(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -203,11 +207,13 @@ func TestStateRmNoArgs(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -262,11 +268,13 @@ func TestStateRmNonExist(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -318,11 +326,13 @@ func TestStateRm_backupExplicit(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -349,11 +359,13 @@ func TestStateRm_noState(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -372,11 +384,13 @@ func TestStateRm_needsInit(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}
@ -446,11 +460,13 @@ func TestStateRm_backendState(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
},
}

View File

@ -1,13 +1,14 @@
package command
import (
"context"
"fmt"
"os"
"strings"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/terraform/tfdiags"
@ -116,12 +117,16 @@ func (c *TaintCommand) Run(args []string) int {
}
if c.stateLock {
stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateMgr, "taint"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
if diags := stateLocker.Lock(stateMgr, "taint"); diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
defer stateLocker.Unlock(nil)
defer func() {
if diags := stateLocker.Unlock(); diags.HasErrors() {
c.showDiagnostics(diags)
}
}()
}
if err := stateMgr.RefreshState(); err != nil {

View File

@ -33,9 +33,11 @@ func TestTaint(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -76,9 +78,11 @@ func TestTaint_lockedState(t *testing.T) {
}
defer unlock()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -122,9 +126,11 @@ func TestTaint_backup(t *testing.T) {
testStateFileDefault(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -165,9 +171,11 @@ func TestTaint_backupDisable(t *testing.T) {
testStateFileDefault(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -188,9 +196,11 @@ func TestTaint_backupDisable(t *testing.T) {
func TestTaint_badState(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -229,9 +239,11 @@ func TestTaint_defaultState(t *testing.T) {
testStateFileDefault(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -271,7 +283,8 @@ func TestTaint_defaultWorkspaceState(t *testing.T) {
path := testStateFileWorkspaceDefault(t, testWorkspace, state)
ui := new(cli.MockUi)
meta := Meta{Ui: ui}
view, _ := testView(t)
meta := Meta{Ui: ui, View: view}
meta.SetWorkspace(testWorkspace)
c := &TaintCommand{
Meta: meta,
@ -308,9 +321,11 @@ func TestTaint_missing(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -344,9 +359,11 @@ func TestTaint_missingAllow(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -399,9 +416,11 @@ func TestTaint_stateOut(t *testing.T) {
testStateFileDefault(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -453,9 +472,11 @@ func TestTaint_module(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -498,10 +519,12 @@ func TestTaint_checkRequiredVersion(t *testing.T) {
path := testStateFile(t, state)
ui := cli.NewMockUi()
view, _ := testView(t)
c := &TaintCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
View: view,
},
}

View File

@ -34,10 +34,12 @@ func TestUnlock(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UnlockCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}
@ -72,9 +74,11 @@ func TestUnlock_inmemBackend(t *testing.T) {
// init backend
ui := new(cli.MockUi)
view, _ := testView(t)
ci := &InitCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
if code := ci.Run(nil); code != 0 {
@ -84,7 +88,8 @@ func TestUnlock_inmemBackend(t *testing.T) {
ui = new(cli.MockUi)
c := &UnlockCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -101,7 +106,8 @@ func TestUnlock_inmemBackend(t *testing.T) {
ui = new(cli.MockUi)
c = &UnlockCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}

View File

@ -1,12 +1,13 @@
package command
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
)
@ -81,12 +82,16 @@ func (c *UntaintCommand) Run(args []string) int {
}
if c.stateLock {
stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateMgr, "untaint"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
if diags := stateLocker.Lock(stateMgr, "untaint"); diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
defer stateLocker.Unlock(nil)
defer func() {
if diags := stateLocker.Unlock(); diags.HasErrors() {
c.showDiagnostics(diags)
}
}()
}
if err := stateMgr.RefreshState(); err != nil {

View File

@ -32,9 +32,11 @@ func TestUntaint(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -80,9 +82,11 @@ func TestUntaint_lockedState(t *testing.T) {
defer unlock()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -126,9 +130,11 @@ func TestUntaint_backup(t *testing.T) {
testStateFileDefault(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -180,9 +186,11 @@ func TestUntaint_backupDisable(t *testing.T) {
testStateFileDefault(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -207,9 +215,11 @@ test_instance.foo:
func TestUntaint_badState(t *testing.T) {
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -248,9 +258,11 @@ func TestUntaint_defaultState(t *testing.T) {
testStateFileDefault(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -295,7 +307,8 @@ func TestUntaint_defaultWorkspaceState(t *testing.T) {
path := testStateFileWorkspaceDefault(t, testWorkspace, state)
ui := new(cli.MockUi)
meta := Meta{Ui: ui}
view, _ := testView(t)
meta := Meta{Ui: ui, View: view}
meta.SetWorkspace(testWorkspace)
c := &UntaintCommand{
Meta: meta,
@ -336,9 +349,11 @@ func TestUntaint_missing(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -372,9 +387,11 @@ func TestUntaint_missingAllow(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -427,9 +444,11 @@ func TestUntaint_stateOut(t *testing.T) {
testStateFileDefault(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -489,9 +508,11 @@ func TestUntaint_module(t *testing.T) {
statePath := testStateFile(t, state)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &UntaintCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}

View File

@ -0,0 +1,40 @@
package views
import (
"fmt"
"github.com/hashicorp/terraform/command/arguments"
)
// The StateLocker view is used to display locking/unlocking status messages
// if the state lock process takes longer than expected.
type StateLocker interface {
Locking()
Unlocking()
}
// NewStateLocker returns an initialized StateLocker implementation for the given ViewType.
func NewStateLocker(vt arguments.ViewType, view *View) StateLocker {
switch vt {
case arguments.ViewHuman:
return &StateLockerHuman{View: *view}
default:
panic(fmt.Sprintf("unknown view type %v", vt))
}
}
// StateLockerHuman is an implementation of StateLocker which prints status to
// a terminal.
type StateLockerHuman struct {
View
}
var _ StateLocker = (*StateLockerHuman)(nil)
func (v *StateLockerHuman) Locking() {
v.streams.Println("Acquiring state lock. This may take a few moments...")
}
func (v *StateLockerHuman) Unlocking() {
v.streams.Println("Releasing state lock. This may take a few moments...")
}

View File

@ -34,7 +34,8 @@ func TestWorkspace_createAndChange(t *testing.T) {
args := []string{"test"}
ui := new(cli.MockUi)
newCmd.Meta = Meta{Ui: ui}
view, _ := testView(t)
newCmd.Meta = Meta{Ui: ui, View: view}
if code := newCmd.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
}
@ -47,7 +48,7 @@ func TestWorkspace_createAndChange(t *testing.T) {
selCmd := &WorkspaceSelectCommand{}
args = []string{backend.DefaultStateName}
ui = new(cli.MockUi)
selCmd.Meta = Meta{Ui: ui}
selCmd.Meta = Meta{Ui: ui, View: view}
if code := selCmd.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
}
@ -83,8 +84,9 @@ func TestWorkspace_createAndList(t *testing.T) {
// create multiple workspaces
for _, env := range envs {
ui := new(cli.MockUi)
view, _ := testView(t)
newCmd := &WorkspaceNewCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
if code := newCmd.Run([]string{env}); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
@ -93,7 +95,8 @@ func TestWorkspace_createAndList(t *testing.T) {
listCmd := &WorkspaceListCommand{}
ui := new(cli.MockUi)
listCmd.Meta = Meta{Ui: ui}
view, _ := testView(t)
listCmd.Meta = Meta{Ui: ui, View: view}
if code := listCmd.Run(nil); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
@ -128,7 +131,8 @@ func TestWorkspace_createAndShow(t *testing.T) {
// make sure current workspace show outputs "default"
showCmd := &WorkspaceShowCommand{}
ui := new(cli.MockUi)
showCmd.Meta = Meta{Ui: ui}
view, _ := testView(t)
showCmd.Meta = Meta{Ui: ui, View: view}
if code := showCmd.Run(nil); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
@ -147,21 +151,21 @@ func TestWorkspace_createAndShow(t *testing.T) {
// create test_a workspace
ui = new(cli.MockUi)
newCmd.Meta = Meta{Ui: ui}
newCmd.Meta = Meta{Ui: ui, View: view}
if code := newCmd.Run(env); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
}
selCmd := &WorkspaceSelectCommand{}
ui = new(cli.MockUi)
selCmd.Meta = Meta{Ui: ui}
selCmd.Meta = Meta{Ui: ui, View: view}
if code := selCmd.Run(env); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
}
showCmd = &WorkspaceShowCommand{}
ui = new(cli.MockUi)
showCmd.Meta = Meta{Ui: ui}
showCmd.Meta = Meta{Ui: ui, View: view}
if code := showCmd.Run(nil); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
@ -188,8 +192,9 @@ func TestWorkspace_createInvalid(t *testing.T) {
// create multiple workspaces
for _, env := range envs {
ui := new(cli.MockUi)
view, _ := testView(t)
newCmd := &WorkspaceNewCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
if code := newCmd.Run([]string{env}); code == 0 {
t.Fatalf("expected failure: \n%s", ui.OutputWriter)
@ -199,7 +204,8 @@ func TestWorkspace_createInvalid(t *testing.T) {
// list workspaces to make sure none were created
listCmd := &WorkspaceListCommand{}
ui := new(cli.MockUi)
listCmd.Meta = Meta{Ui: ui}
view, _ := testView(t)
listCmd.Meta = Meta{Ui: ui, View: view}
if code := listCmd.Run(nil); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
@ -222,8 +228,9 @@ func TestWorkspace_createWithState(t *testing.T) {
// init the backend
ui := new(cli.MockUi)
view, _ := testView(t)
initCmd := &InitCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
if code := initCmd.Run([]string{}); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
@ -257,7 +264,7 @@ func TestWorkspace_createWithState(t *testing.T) {
args := []string{"-state", "test.tfstate", workspace}
ui = new(cli.MockUi)
newCmd := &WorkspaceNewCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
if code := newCmd.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
@ -303,8 +310,9 @@ func TestWorkspace_delete(t *testing.T) {
}
ui := new(cli.MockUi)
view, _ := testView(t)
delCmd := &WorkspaceDeleteCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
current, _ := delCmd.Workspace()
@ -352,8 +360,9 @@ func TestWorkspace_deleteInvalid(t *testing.T) {
}
ui := new(cli.MockUi)
view, _ := testView(t)
delCmd := &WorkspaceDeleteCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
// delete the workspace
@ -406,8 +415,9 @@ func TestWorkspace_deleteWithState(t *testing.T) {
}
ui := new(cli.MockUi)
view, _ := testView(t)
delCmd := &WorkspaceDeleteCommand{
Meta: Meta{Ui: ui},
Meta: Meta{Ui: ui, View: view},
}
args := []string{"test"}
if code := delCmd.Run(args); code == 0 {

View File

@ -1,12 +1,13 @@
package command
import (
"context"
"fmt"
"strings"
"time"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/tfdiags"
"github.com/mitchellh/cli"
"github.com/posener/complete"
@ -107,9 +108,9 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
var stateLocker clistate.Locker
if stateLock {
stateLocker = clistate.NewLocker(context.Background(), stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateMgr, "workspace_delete"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
stateLocker = clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
if diags := stateLocker.Lock(stateMgr, "state-replace-provider"); diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
} else {
@ -118,7 +119,7 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
if err := stateMgr.RefreshState(); err != nil {
// We need to release the lock before exit
stateLocker.Unlock(nil)
stateLocker.Unlock()
c.Ui.Error(err.Error())
return 1
}
@ -127,7 +128,7 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
if hasResources && !force {
// We need to release the lock before exit
stateLocker.Unlock(nil)
stateLocker.Unlock()
c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envNotEmpty), workspace))
return 1
}
@ -141,7 +142,7 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
// state deletion, i.e. in a CI environment. Adding Delete() as a
// required method of States would allow the removal of the resource to
// be delegated from the Backend to the State itself.
stateLocker.Unlock(nil)
stateLocker.Unlock()
err = b.DeleteWorkspace(workspace)
if err != nil {

View File

@ -1,13 +1,14 @@
package command
import (
"context"
"fmt"
"os"
"strings"
"time"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/states/statefile"
"github.com/hashicorp/terraform/tfdiags"
"github.com/mitchellh/cli"
@ -124,12 +125,16 @@ func (c *WorkspaceNewCommand) Run(args []string) int {
}
if stateLock {
stateLocker := clistate.NewLocker(context.Background(), stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateMgr, "workspace_new"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
if diags := stateLocker.Lock(stateMgr, "workspace-new"); diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
defer stateLocker.Unlock(nil)
defer func() {
if diags := stateLocker.Unlock(); diags.HasErrors() {
c.showDiagnostics(diags)
}
}()
}
// read the existing state file