Merge pull request #13262 from hashicorp/jbardin/lock-timeouts
lock timeouts
This commit is contained in:
commit
d059939f88
|
@ -7,6 +7,7 @@ package backend
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"github.com/hashicorp/terraform/config/module"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
|
@ -132,6 +133,9 @@ type Operation struct {
|
||||||
// state.Lockers for its duration, and Unlock when complete.
|
// state.Lockers for its duration, and Unlock when complete.
|
||||||
LockState bool
|
LockState bool
|
||||||
|
|
||||||
|
// The duration to retry obtaining a State lock.
|
||||||
|
StateLockTimeout time.Duration
|
||||||
|
|
||||||
// Environment is the named state that should be loaded from the Backend.
|
// Environment is the named state that should be loaded from the Backend.
|
||||||
Environment string
|
Environment string
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"github.com/hashicorp/terraform/config/module"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
@ -52,9 +52,12 @@ func (b *Local) opApply(
|
||||||
}
|
}
|
||||||
|
|
||||||
if op.LockState {
|
if op.LockState {
|
||||||
|
lockCtx, cancel := context.WithTimeout(ctx, op.StateLockTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
lockInfo := state.NewLockInfo()
|
lockInfo := state.NewLockInfo()
|
||||||
lockInfo.Operation = op.Type.String()
|
lockInfo.Operation = op.Type.String()
|
||||||
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
lockID, err := clistate.Lock(lockCtx, opState, lockInfo, b.CLI, b.Colorize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
"github.com/hashicorp/terraform/command/format"
|
"github.com/hashicorp/terraform/command/format"
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"github.com/hashicorp/terraform/config/module"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
@ -61,9 +61,12 @@ func (b *Local) opPlan(
|
||||||
}
|
}
|
||||||
|
|
||||||
if op.LockState {
|
if op.LockState {
|
||||||
|
lockCtx, cancel := context.WithTimeout(ctx, op.StateLockTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
lockInfo := state.NewLockInfo()
|
lockInfo := state.NewLockInfo()
|
||||||
lockInfo.Operation = op.Type.String()
|
lockInfo.Operation = op.Type.String()
|
||||||
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
lockID, err := clistate.Lock(lockCtx, opState, lockInfo, b.CLI, b.Colorize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"github.com/hashicorp/terraform/config/module"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
)
|
)
|
||||||
|
@ -51,9 +51,12 @@ func (b *Local) opRefresh(
|
||||||
}
|
}
|
||||||
|
|
||||||
if op.LockState {
|
if op.LockState {
|
||||||
|
lockCtx, cancel := context.WithTimeout(ctx, op.StateLockTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
lockInfo := state.NewLockInfo()
|
lockInfo := state.NewLockInfo()
|
||||||
lockInfo.Operation = op.Type.String()
|
lockInfo.Operation = op.Type.String()
|
||||||
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
lockID, err := clistate.Lock(lockCtx, opState, lockInfo, b.CLI, b.Colorize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -102,22 +102,19 @@ func (b *Backend) State(name string) (state.State, error) {
|
||||||
stateMgr = &state.LockDisabled{Inner: stateMgr}
|
stateMgr = &state.LockDisabled{Inner: stateMgr}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the locker, which we know always exists
|
|
||||||
stateMgrLocker := stateMgr.(state.Locker)
|
|
||||||
|
|
||||||
// Grab a lock, we use this to write an empty state if one doesn't
|
// Grab a lock, we use this to write an empty state if one doesn't
|
||||||
// exist already. We have to write an empty state as a sentinel value
|
// exist already. We have to write an empty state as a sentinel value
|
||||||
// so States() knows it exists.
|
// so States() knows it exists.
|
||||||
lockInfo := state.NewLockInfo()
|
lockInfo := state.NewLockInfo()
|
||||||
lockInfo.Operation = "init"
|
lockInfo.Operation = "init"
|
||||||
lockId, err := stateMgrLocker.Lock(lockInfo)
|
lockId, err := stateMgr.Lock(lockInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to lock state in Consul: %s", err)
|
return nil, fmt.Errorf("failed to lock state in Consul: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local helper function so we can call it multiple places
|
// Local helper function so we can call it multiple places
|
||||||
lockUnlock := func(parent error) error {
|
lockUnlock := func(parent error) error {
|
||||||
if err := stateMgrLocker.Unlock(lockId); err != nil {
|
if err := stateMgr.Unlock(lockId); err != nil {
|
||||||
return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
|
return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ func (c *ApplyCommand) Run(args []string) int {
|
||||||
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
|
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
|
||||||
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
||||||
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
||||||
|
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
|
||||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
|
@ -183,7 +184,6 @@ func (c *ApplyCommand) Run(args []string) int {
|
||||||
opReq.Plan = plan
|
opReq.Plan = plan
|
||||||
opReq.PlanRefresh = refresh
|
opReq.PlanRefresh = refresh
|
||||||
opReq.Type = backend.OperationTypeApply
|
opReq.Type = backend.OperationTypeApply
|
||||||
opReq.LockState = c.Meta.stateLock
|
|
||||||
|
|
||||||
// Perform the operation
|
// Perform the operation
|
||||||
ctx, ctxCancel := context.WithCancel(context.Background())
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
@ -276,6 +276,8 @@ Options:
|
||||||
|
|
||||||
-lock=true Lock the state file when locking is supported.
|
-lock=true Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
-lock-timeout=0s Duration to retry a state lock.
|
||||||
|
|
||||||
-input=true Ask for input for variables if not directly set.
|
-input=true Ask for input for variables if not directly set.
|
||||||
|
|
||||||
-no-color If specified, output won't contain any color.
|
-no-color If specified, output won't contain any color.
|
||||||
|
@ -325,6 +327,8 @@ Options:
|
||||||
|
|
||||||
-lock=true Lock the state file when locking is supported.
|
-lock=true Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
-lock-timeout=0s Duration to retry a state lock.
|
||||||
|
|
||||||
-no-color If specified, output won't contain any color.
|
-no-color If specified, output won't contain any color.
|
||||||
|
|
||||||
-parallelism=n Limit the number of concurrent operations.
|
-parallelism=n Limit the number of concurrent operations.
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -92,6 +93,42 @@ func TestApply_lockedState(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test apply with locked state, waiting for unlock
|
||||||
|
func TestApply_lockedStateWait(t *testing.T) {
|
||||||
|
statePath := testTempFile(t)
|
||||||
|
|
||||||
|
unlock, err := testLockState("./testdata", statePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlock during apply
|
||||||
|
go func() {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
p := testProvider()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ApplyCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
ContextOpts: testCtxConfig(p),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait 4s just in case the lock process doesn't release in under a second,
|
||||||
|
// and we want our context to be alive for a second retry at the 3s mark.
|
||||||
|
args := []string{
|
||||||
|
"-state", statePath,
|
||||||
|
"-lock-timeout", "4s",
|
||||||
|
testFixturePath("apply"),
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
log.Fatalf("lock should have succeed in less than 3s: %s", ui.ErrorWriter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// high water mark counter
|
// high water mark counter
|
||||||
type hwm struct {
|
type hwm struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
//
|
//
|
||||||
// This is a separate package so that backends can use this for consistent
|
// This is a separate package so that backends can use this for consistent
|
||||||
// messaging without creating a circular reference to the command package.
|
// messaging without creating a circular reference to the command package.
|
||||||
package message
|
package clistate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -48,17 +49,13 @@ that no one else is holding a lock.
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lock locks the given state and outputs to the user if locking
|
// Lock locks the given state and outputs to the user if locking
|
||||||
// is taking longer than the threshold.
|
// is taking longer than the threshold. The lock is retried until the context
|
||||||
func Lock(s state.State, info *state.LockInfo, ui cli.Ui, color *colorstring.Colorize) (string, error) {
|
// is cancelled.
|
||||||
sl, ok := s.(state.Locker)
|
func Lock(ctx context.Context, s state.State, info *state.LockInfo, ui cli.Ui, color *colorstring.Colorize) (string, error) {
|
||||||
if !ok {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var lockID string
|
var lockID string
|
||||||
|
|
||||||
err := slowmessage.Do(LockThreshold, func() error {
|
err := slowmessage.Do(LockThreshold, func() error {
|
||||||
id, err := sl.Lock(info)
|
id, err := state.LockWithContext(ctx, s, info)
|
||||||
lockID = id
|
lockID = id
|
||||||
return err
|
return err
|
||||||
}, func() {
|
}, func() {
|
||||||
|
@ -77,13 +74,8 @@ func Lock(s state.State, info *state.LockInfo, ui cli.Ui, color *colorstring.Col
|
||||||
// Unlock unlocks the given state and outputs to the user if the
|
// Unlock unlocks the given state and outputs to the user if the
|
||||||
// unlock fails what can be done.
|
// unlock fails what can be done.
|
||||||
func Unlock(s state.State, id string, ui cli.Ui, color *colorstring.Colorize) error {
|
func Unlock(s state.State, id string, ui cli.Ui, color *colorstring.Colorize) error {
|
||||||
sl, ok := s.(state.Locker)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := slowmessage.Do(LockThreshold, func() error {
|
err := slowmessage.Do(LockThreshold, func() error {
|
||||||
return sl.Unlock(id)
|
return s.Unlock(id)
|
||||||
}, func() {
|
}, func() {
|
||||||
if ui != nil {
|
if ui != nil {
|
||||||
ui.Output(color.Color(UnlockMessage))
|
ui.Output(color.Color(UnlockMessage))
|
|
@ -1,13 +1,13 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
|
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type EnvDeleteCommand struct {
|
type EnvDeleteCommand struct {
|
||||||
|
@ -93,15 +93,20 @@ func (c *EnvDeleteCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock the state if we can
|
if c.stateLock {
|
||||||
lockInfo := state.NewLockInfo()
|
lockCtx, cancel := context.WithTimeout(context.Background(), c.stateLockTimeout)
|
||||||
lockInfo.Operation = "env delete"
|
defer cancel()
|
||||||
lockID, err := clistate.Lock(sMgr, lockInfo, c.Ui, c.Colorize())
|
|
||||||
if err != nil {
|
// Lock the state if we can
|
||||||
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
lockInfo := state.NewLockInfo()
|
||||||
return 1
|
lockInfo.Operation = "env delete"
|
||||||
|
lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, c.Ui, c.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
defer clistate.Unlock(sMgr, lockID, c.Ui, c.Colorize())
|
||||||
}
|
}
|
||||||
defer clistate.Unlock(sMgr, lockID, c.Ui, c.Colorize())
|
|
||||||
|
|
||||||
err = b.DeleteState(delEnv)
|
err = b.DeleteState(delEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
|
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type EnvNewCommand struct {
|
type EnvNewCommand struct {
|
||||||
|
@ -88,15 +88,20 @@ func (c *EnvNewCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock the state if we can
|
if c.stateLock {
|
||||||
lockInfo := state.NewLockInfo()
|
lockCtx, cancel := context.WithTimeout(context.Background(), c.stateLockTimeout)
|
||||||
lockInfo.Operation = "env new"
|
defer cancel()
|
||||||
lockID, err := clistate.Lock(sMgr, lockInfo, c.Ui, c.Colorize())
|
|
||||||
if err != nil {
|
// Lock the state if we can
|
||||||
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
lockInfo := state.NewLockInfo()
|
||||||
return 1
|
lockInfo.Operation = "env new"
|
||||||
|
lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, c.Ui, c.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
defer clistate.Unlock(sMgr, lockID, c.Ui, c.Colorize())
|
||||||
}
|
}
|
||||||
defer clistate.Unlock(sMgr, lockID, c.Ui, c.Colorize())
|
|
||||||
|
|
||||||
// read the existing state file
|
// read the existing state file
|
||||||
stateFile, err := os.Open(statePath)
|
stateFile, err := os.Open(statePath)
|
||||||
|
|
|
@ -35,6 +35,8 @@ func (c *ImportCommand) Run(args []string) int {
|
||||||
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
||||||
cmdFlags.StringVar(&configPath, "config", pwd, "path")
|
cmdFlags.StringVar(&configPath, "config", pwd, "path")
|
||||||
cmdFlags.StringVar(&c.Meta.provider, "provider", "", "provider")
|
cmdFlags.StringVar(&c.Meta.provider, "provider", "", "provider")
|
||||||
|
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
||||||
|
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
|
||||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
|
@ -162,6 +164,10 @@ Options:
|
||||||
|
|
||||||
-input=true Ask for input for variables if not directly set.
|
-input=true Ask for input for variables if not directly set.
|
||||||
|
|
||||||
|
-lock=true Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
-lock-timeout=0s Duration to retry a state lock.
|
||||||
|
|
||||||
-no-color If specified, output won't contain any color.
|
-no-color If specified, output won't contain any color.
|
||||||
|
|
||||||
-provider=provider Specific provider to use for import. This is used for
|
-provider=provider Specific provider to use for import. This is used for
|
||||||
|
|
|
@ -28,6 +28,8 @@ func (c *InitCommand) Run(args []string) int {
|
||||||
cmdFlags.Var((*variables.FlagAny)(&flagConfigExtra), "backend-config", "")
|
cmdFlags.Var((*variables.FlagAny)(&flagConfigExtra), "backend-config", "")
|
||||||
cmdFlags.BoolVar(&flagGet, "get", true, "")
|
cmdFlags.BoolVar(&flagGet, "get", true, "")
|
||||||
cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data")
|
cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data")
|
||||||
|
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
||||||
|
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
|
||||||
|
|
||||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
|
@ -226,6 +228,10 @@ Options:
|
||||||
-input=true Ask for input if necessary. If false, will error if
|
-input=true Ask for input if necessary. If false, will error if
|
||||||
input was required.
|
input was required.
|
||||||
|
|
||||||
|
-lock=true Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
-lock-timeout=0s Duration to retry a state lock.
|
||||||
|
|
||||||
-no-color If specified, output won't contain any color.
|
-no-color If specified, output won't contain any color.
|
||||||
|
|
||||||
-force-copy Suppress prompts about copying state data. This is
|
-force-copy Suppress prompts about copying state data. This is
|
||||||
|
|
|
@ -90,16 +90,20 @@ type Meta struct {
|
||||||
//
|
//
|
||||||
// stateLock is set to false to disable state locking
|
// stateLock is set to false to disable state locking
|
||||||
//
|
//
|
||||||
|
// stateLockTimeout is the optional duration to retry a state locks locks
|
||||||
|
// when it is already locked by another process.
|
||||||
|
//
|
||||||
// forceInitCopy suppresses confirmation for copying state data during
|
// forceInitCopy suppresses confirmation for copying state data during
|
||||||
// init.
|
// init.
|
||||||
statePath string
|
statePath string
|
||||||
stateOutPath string
|
stateOutPath string
|
||||||
backupPath string
|
backupPath string
|
||||||
parallelism int
|
parallelism int
|
||||||
shadow bool
|
shadow bool
|
||||||
provider string
|
provider string
|
||||||
stateLock bool
|
stateLock bool
|
||||||
forceInitCopy bool
|
stateLockTimeout time.Duration
|
||||||
|
forceInitCopy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// initStatePaths is used to initialize the default values for
|
// initStatePaths is used to initialize the default values for
|
||||||
|
|
|
@ -4,6 +4,7 @@ package command
|
||||||
// exported and private.
|
// exported and private.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -16,13 +17,13 @@ import (
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/hcl"
|
"github.com/hashicorp/hcl"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
backendinit "github.com/hashicorp/terraform/backend/init"
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
|
backendinit "github.com/hashicorp/terraform/backend/init"
|
||||||
backendlocal "github.com/hashicorp/terraform/backend/local"
|
backendlocal "github.com/hashicorp/terraform/backend/local"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -166,10 +167,12 @@ func (m *Meta) IsLocalBackend(b backend.Backend) bool {
|
||||||
// be called.
|
// be called.
|
||||||
func (m *Meta) Operation() *backend.Operation {
|
func (m *Meta) Operation() *backend.Operation {
|
||||||
return &backend.Operation{
|
return &backend.Operation{
|
||||||
PlanOutBackend: m.backendState,
|
PlanOutBackend: m.backendState,
|
||||||
Targets: m.targets,
|
Targets: m.targets,
|
||||||
UIIn: m.UIInput(),
|
UIIn: m.UIInput(),
|
||||||
Environment: m.Env(),
|
Environment: m.Env(),
|
||||||
|
LockState: m.stateLock,
|
||||||
|
StateLockTimeout: m.stateLockTimeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,15 +612,20 @@ func (m *Meta) backendFromPlan(opts *BackendOpts) (backend.Backend, error) {
|
||||||
return nil, fmt.Errorf("Error reading state: %s", err)
|
return nil, fmt.Errorf("Error reading state: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock the state if we can
|
if m.stateLock {
|
||||||
lockInfo := state.NewLockInfo()
|
lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
|
||||||
lockInfo.Operation = "backend from plan"
|
defer cancel()
|
||||||
|
|
||||||
lockID, err := clistate.Lock(realMgr, lockInfo, m.Ui, m.Colorize())
|
// Lock the state if we can
|
||||||
if err != nil {
|
lockInfo := state.NewLockInfo()
|
||||||
return nil, fmt.Errorf("Error locking state: %s", err)
|
lockInfo.Operation = "backend from plan"
|
||||||
|
|
||||||
|
lockID, err := clistate.Lock(lockCtx, realMgr, lockInfo, m.Ui, m.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error locking state: %s", err)
|
||||||
|
}
|
||||||
|
defer clistate.Unlock(realMgr, lockID, m.Ui, m.Colorize())
|
||||||
}
|
}
|
||||||
defer clistate.Unlock(realMgr, lockID, m.Ui, m.Colorize())
|
|
||||||
|
|
||||||
if err := realMgr.RefreshState(); err != nil {
|
if err := realMgr.RefreshState(); err != nil {
|
||||||
return nil, fmt.Errorf("Error reading state: %s", err)
|
return nil, fmt.Errorf("Error reading state: %s", err)
|
||||||
|
@ -1040,15 +1048,20 @@ func (m *Meta) backend_C_r_s(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock the state if we can
|
if m.stateLock {
|
||||||
lockInfo := state.NewLockInfo()
|
lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
|
||||||
lockInfo.Operation = "backend from config"
|
defer cancel()
|
||||||
|
|
||||||
lockID, err := clistate.Lock(sMgr, lockInfo, m.Ui, m.Colorize())
|
// Lock the state if we can
|
||||||
if err != nil {
|
lockInfo := state.NewLockInfo()
|
||||||
return nil, fmt.Errorf("Error locking state: %s", err)
|
lockInfo.Operation = "backend from config"
|
||||||
|
|
||||||
|
lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, m.Ui, m.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error locking state: %s", err)
|
||||||
|
}
|
||||||
|
defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
|
||||||
}
|
}
|
||||||
defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
|
|
||||||
|
|
||||||
// Store the metadata in our saved state location
|
// Store the metadata in our saved state location
|
||||||
s := sMgr.State()
|
s := sMgr.State()
|
||||||
|
@ -1132,15 +1145,20 @@ func (m *Meta) backend_C_r_S_changed(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock the state if we can
|
if m.stateLock {
|
||||||
lockInfo := state.NewLockInfo()
|
lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
|
||||||
lockInfo.Operation = "backend from config"
|
defer cancel()
|
||||||
|
|
||||||
lockID, err := clistate.Lock(sMgr, lockInfo, m.Ui, m.Colorize())
|
// Lock the state if we can
|
||||||
if err != nil {
|
lockInfo := state.NewLockInfo()
|
||||||
return nil, fmt.Errorf("Error locking state: %s", err)
|
lockInfo.Operation = "backend from config"
|
||||||
|
|
||||||
|
lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, m.Ui, m.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error locking state: %s", err)
|
||||||
|
}
|
||||||
|
defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
|
||||||
}
|
}
|
||||||
defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
|
|
||||||
|
|
||||||
// Update the backend state
|
// Update the backend state
|
||||||
s = sMgr.State()
|
s = sMgr.State()
|
||||||
|
@ -1288,15 +1306,20 @@ func (m *Meta) backend_C_R_S_unchanged(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock the state if we can
|
if m.stateLock {
|
||||||
lockInfo := state.NewLockInfo()
|
lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
|
||||||
lockInfo.Operation = "backend from config"
|
defer cancel()
|
||||||
|
|
||||||
lockID, err := clistate.Lock(sMgr, lockInfo, m.Ui, m.Colorize())
|
// Lock the state if we can
|
||||||
if err != nil {
|
lockInfo := state.NewLockInfo()
|
||||||
return nil, fmt.Errorf("Error locking state: %s", err)
|
lockInfo.Operation = "backend from config"
|
||||||
|
|
||||||
|
lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, m.Ui, m.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error locking state: %s", err)
|
||||||
|
}
|
||||||
|
defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
|
||||||
}
|
}
|
||||||
defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
|
|
||||||
|
|
||||||
// Unset the remote state
|
// Unset the remote state
|
||||||
s = sMgr.State()
|
s = sMgr.State()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -9,7 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
@ -217,25 +218,30 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
|
||||||
errMigrateSingleLoadDefault), opts.TwoType, err)
|
errMigrateSingleLoadDefault), opts.TwoType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
lockInfoOne := state.NewLockInfo()
|
if m.stateLock {
|
||||||
lockInfoOne.Operation = "migration"
|
lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
|
||||||
lockInfoOne.Info = "source state"
|
defer cancel()
|
||||||
|
|
||||||
lockIDOne, err := clistate.Lock(stateOne, lockInfoOne, m.Ui, m.Colorize())
|
lockInfoOne := state.NewLockInfo()
|
||||||
if err != nil {
|
lockInfoOne.Operation = "migration"
|
||||||
return fmt.Errorf("Error locking source state: %s", err)
|
lockInfoOne.Info = "source state"
|
||||||
|
|
||||||
|
lockIDOne, err := clistate.Lock(lockCtx, stateOne, lockInfoOne, m.Ui, m.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error locking source state: %s", err)
|
||||||
|
}
|
||||||
|
defer clistate.Unlock(stateOne, lockIDOne, m.Ui, m.Colorize())
|
||||||
|
|
||||||
|
lockInfoTwo := state.NewLockInfo()
|
||||||
|
lockInfoTwo.Operation = "migration"
|
||||||
|
lockInfoTwo.Info = "destination state"
|
||||||
|
|
||||||
|
lockIDTwo, err := clistate.Lock(lockCtx, stateTwo, lockInfoTwo, m.Ui, m.Colorize())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error locking destination state: %s", err)
|
||||||
|
}
|
||||||
|
defer clistate.Unlock(stateTwo, lockIDTwo, m.Ui, m.Colorize())
|
||||||
}
|
}
|
||||||
defer clistate.Unlock(stateOne, lockIDOne, m.Ui, m.Colorize())
|
|
||||||
|
|
||||||
lockInfoTwo := state.NewLockInfo()
|
|
||||||
lockInfoTwo.Operation = "migration"
|
|
||||||
lockInfoTwo.Info = "destination state"
|
|
||||||
|
|
||||||
lockIDTwo, err := clistate.Lock(stateTwo, lockInfoTwo, m.Ui, m.Colorize())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error locking destination state: %s", err)
|
|
||||||
}
|
|
||||||
defer clistate.Unlock(stateTwo, lockIDTwo, m.Ui, m.Colorize())
|
|
||||||
|
|
||||||
one := stateOne.State()
|
one := stateOne.State()
|
||||||
two := stateTwo.State()
|
two := stateTwo.State()
|
||||||
|
|
|
@ -32,6 +32,7 @@ func (c *PlanCommand) Run(args []string) int {
|
||||||
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
|
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
|
||||||
cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode")
|
cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode")
|
||||||
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
||||||
|
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
|
||||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
|
@ -85,7 +86,6 @@ func (c *PlanCommand) Run(args []string) int {
|
||||||
opReq.PlanRefresh = refresh
|
opReq.PlanRefresh = refresh
|
||||||
opReq.PlanOutPath = outPath
|
opReq.PlanOutPath = outPath
|
||||||
opReq.Type = backend.OperationTypePlan
|
opReq.Type = backend.OperationTypePlan
|
||||||
opReq.LockState = c.Meta.stateLock
|
|
||||||
|
|
||||||
// Perform the operation
|
// Perform the operation
|
||||||
op, err := b.Operation(context.Background(), opReq)
|
op, err := b.Operation(context.Background(), opReq)
|
||||||
|
@ -145,6 +145,8 @@ Options:
|
||||||
|
|
||||||
-lock=true Lock the state file when locking is supported.
|
-lock=true Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
-lock-timeout=0s Duration to retry a state lock.
|
||||||
|
|
||||||
-module-depth=n Specifies the depth of modules to show in the output.
|
-module-depth=n Specifies the depth of modules to show in the output.
|
||||||
This does not affect the plan itself, only the output
|
This does not affect the plan itself, only the output
|
||||||
shown. By default, this is -1, which will expand all.
|
shown. By default, this is -1, which will expand all.
|
||||||
|
|
|
@ -24,6 +24,7 @@ func (c *RefreshCommand) Run(args []string) int {
|
||||||
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
|
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
|
||||||
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
||||||
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
||||||
|
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
|
||||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
|
@ -53,7 +54,6 @@ func (c *RefreshCommand) Run(args []string) int {
|
||||||
opReq := c.Operation()
|
opReq := c.Operation()
|
||||||
opReq.Type = backend.OperationTypeRefresh
|
opReq.Type = backend.OperationTypeRefresh
|
||||||
opReq.Module = mod
|
opReq.Module = mod
|
||||||
opReq.LockState = c.Meta.stateLock
|
|
||||||
|
|
||||||
// Perform the operation
|
// Perform the operation
|
||||||
op, err := b.Operation(context.Background(), opReq)
|
op, err := b.Operation(context.Background(), opReq)
|
||||||
|
@ -98,6 +98,8 @@ Options:
|
||||||
|
|
||||||
-lock=true Lock the state file when locking is supported.
|
-lock=true Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
-lock-timeout=0s Duration to retry a state lock.
|
||||||
|
|
||||||
-no-color If specified, output won't contain any color.
|
-no-color If specified, output won't contain any color.
|
||||||
|
|
||||||
-state=path Path to read and save state (unless state-out
|
-state=path Path to read and save state (unless state-out
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
@ -28,6 +29,7 @@ func (c *TaintCommand) Run(args []string) int {
|
||||||
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
|
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
|
||||||
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
||||||
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
||||||
|
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
|
||||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
|
@ -78,10 +80,13 @@ func (c *TaintCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Meta.stateLock {
|
if c.stateLock {
|
||||||
|
lockCtx, cancel := context.WithTimeout(context.Background(), c.stateLockTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
lockInfo := state.NewLockInfo()
|
lockInfo := state.NewLockInfo()
|
||||||
lockInfo.Operation = "taint"
|
lockInfo.Operation = "taint"
|
||||||
lockID, err := clistate.Lock(st, lockInfo, c.Ui, c.Colorize())
|
lockID, err := clistate.Lock(lockCtx, st, lockInfo, c.Ui, c.Colorize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
|
@ -188,6 +193,8 @@ Options:
|
||||||
|
|
||||||
-lock=true Lock the state file when locking is supported.
|
-lock=true Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
-lock-timeout=0s Duration to retry a state lock.
|
||||||
|
|
||||||
-module=path The module path where the resource lives. By
|
-module=path The module path where the resource lives. By
|
||||||
default this will be root. Child modules can be specified
|
default this will be root. Child modules can be specified
|
||||||
by names. Ex. "consul" or "consul.vpc" (nested modules).
|
by names. Ex. "consul" or "consul.vpc" (nested modules).
|
||||||
|
|
|
@ -59,13 +59,6 @@ func (c *UnlockCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
s, ok := st.(state.Locker)
|
|
||||||
if !ok {
|
|
||||||
c.Ui.Error("The remote state backend in use does not support locking, and therefor\n" +
|
|
||||||
"cannot be unlocked.")
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
isLocal := false
|
isLocal := false
|
||||||
switch s := st.(type) {
|
switch s := st.(type) {
|
||||||
case *state.BackupState:
|
case *state.BackupState:
|
||||||
|
@ -103,7 +96,7 @@ func (c *UnlockCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Unlock(lockID); err != nil {
|
if err := st.Unlock(lockID); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Failed to unlock state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Failed to unlock state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
clistate "github.com/hashicorp/terraform/command/state"
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ func (c *UntaintCommand) Run(args []string) int {
|
||||||
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
|
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
|
||||||
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
|
||||||
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
||||||
|
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
|
||||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
|
@ -66,10 +68,13 @@ func (c *UntaintCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Meta.stateLock {
|
if c.stateLock {
|
||||||
|
lockCtx, cancel := context.WithTimeout(context.Background(), c.stateLockTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
lockInfo := state.NewLockInfo()
|
lockInfo := state.NewLockInfo()
|
||||||
lockInfo.Operation = "untaint"
|
lockInfo.Operation = "untaint"
|
||||||
lockID, err := clistate.Lock(st, lockInfo, c.Ui, c.Colorize())
|
lockID, err := clistate.Lock(lockCtx, st, lockInfo, c.Ui, c.Colorize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
|
@ -176,6 +181,8 @@ Options:
|
||||||
|
|
||||||
-lock=true Lock the state file when locking is supported.
|
-lock=true Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
-lock-timeout=0s Duration to retry a state lock.
|
||||||
|
|
||||||
-module=path The module path where the resource lives. By
|
-module=path The module path where the resource lives. By
|
||||||
default this will be root. Child modules can be specified
|
default this will be root. Child modules can be specified
|
||||||
by names. Ex. "consul" or "consul.vpc" (nested modules).
|
by names. Ex. "consul" or "consul.vpc" (nested modules).
|
||||||
|
|
17
commands.go
17
commands.go
|
@ -42,8 +42,9 @@ func init() {
|
||||||
// that to match.
|
// that to match.
|
||||||
|
|
||||||
PlumbingCommands = map[string]struct{}{
|
PlumbingCommands = map[string]struct{}{
|
||||||
"state": struct{}{}, // includes all subcommands
|
"state": struct{}{}, // includes all subcommands
|
||||||
"debug": struct{}{}, // includes all subcommands
|
"debug": struct{}{}, // includes all subcommands
|
||||||
|
"force-unlock": struct{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands = map[string]cli.CommandFactory{
|
Commands = map[string]cli.CommandFactory{
|
||||||
|
@ -105,12 +106,6 @@ func init() {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
|
||||||
"force-unlock": func() (cli.Command, error) {
|
|
||||||
return &command.UnlockCommand{
|
|
||||||
Meta: meta,
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
|
|
||||||
"get": func() (cli.Command, error) {
|
"get": func() (cli.Command, error) {
|
||||||
return &command.GetCommand{
|
return &command.GetCommand{
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
|
@ -215,6 +210,12 @@ func init() {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"force-unlock": func() (cli.Command, error) {
|
||||||
|
return &command.UnlockCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
|
||||||
"state": func() (cli.Command, error) {
|
"state": func() (cli.Command, error) {
|
||||||
return &command.StateCommand{}, nil
|
return &command.StateCommand{}, nil
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,19 +41,12 @@ func (s *BackupState) PersistState() error {
|
||||||
return s.Real.PersistState()
|
return s.Real.PersistState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// all states get wrapped by BackupState, so it has to be a Locker
|
|
||||||
func (s *BackupState) Lock(info *LockInfo) (string, error) {
|
func (s *BackupState) Lock(info *LockInfo) (string, error) {
|
||||||
if s, ok := s.Real.(Locker); ok {
|
return s.Real.Lock(info)
|
||||||
return s.Lock(info)
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BackupState) Unlock(id string) error {
|
func (s *BackupState) Unlock(id string) error {
|
||||||
if s, ok := s.Real.(Locker); ok {
|
return s.Real.Unlock(id)
|
||||||
return s.Unlock(id)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BackupState) backup() error {
|
func (s *BackupState) backup() error {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,3 +30,59 @@ func (s *InmemState) WriteState(state *terraform.State) error {
|
||||||
func (s *InmemState) PersistState() error {
|
func (s *InmemState) PersistState() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *InmemState) Lock(*LockInfo) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *InmemState) Unlock(string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// inmemLocker is an in-memory State implementation for testing locks.
|
||||||
|
type inmemLocker struct {
|
||||||
|
*InmemState
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
lockInfo *LockInfo
|
||||||
|
// count the calls to Lock
|
||||||
|
lockCounter int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inmemLocker) Lock(info *LockInfo) (string, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
s.lockCounter++
|
||||||
|
|
||||||
|
lockErr := &LockError{
|
||||||
|
Info: &LockInfo{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.lockInfo != nil {
|
||||||
|
lockErr.Err = errors.New("state locked")
|
||||||
|
*lockErr.Info = *s.lockInfo
|
||||||
|
return "", lockErr
|
||||||
|
}
|
||||||
|
|
||||||
|
info.Created = time.Now().UTC()
|
||||||
|
s.lockInfo = info
|
||||||
|
return s.lockInfo.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inmemLocker) Unlock(id string) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
lockErr := &LockError{
|
||||||
|
Info: &LockInfo{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if id != s.lockInfo.ID {
|
||||||
|
lockErr.Err = errors.New("invalid lock id")
|
||||||
|
*lockErr.Info = *s.lockInfo
|
||||||
|
return lockErr
|
||||||
|
}
|
||||||
|
|
||||||
|
s.lockInfo = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -14,3 +14,39 @@ func TestInmemState_impl(t *testing.T) {
|
||||||
var _ StatePersister = new(InmemState)
|
var _ StatePersister = new(InmemState)
|
||||||
var _ StateRefresher = new(InmemState)
|
var _ StateRefresher = new(InmemState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInmemLocker(t *testing.T) {
|
||||||
|
inmem := &InmemState{state: TestStateInitial()}
|
||||||
|
// test that it correctly wraps the inmem state
|
||||||
|
s := &inmemLocker{InmemState: inmem}
|
||||||
|
TestState(t, s)
|
||||||
|
|
||||||
|
info := NewLockInfo()
|
||||||
|
|
||||||
|
id, err := s.Lock(info)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if id == "" {
|
||||||
|
t.Fatal("no lock id from state lock")
|
||||||
|
}
|
||||||
|
|
||||||
|
// locking again should fail
|
||||||
|
_, err = s.Lock(NewLockInfo())
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("state locked while locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.(*LockError).Info.ID != id {
|
||||||
|
t.Fatal("wrong lock id from lock failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Unlock(id); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.Lock(NewLockInfo()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -28,6 +29,7 @@ type State interface {
|
||||||
StateWriter
|
StateWriter
|
||||||
StateRefresher
|
StateRefresher
|
||||||
StatePersister
|
StatePersister
|
||||||
|
Locker
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateReader is the interface for things that can return a state. Retrieving
|
// StateReader is the interface for things that can return a state. Retrieving
|
||||||
|
@ -72,6 +74,48 @@ type Locker interface {
|
||||||
Unlock(id string) error
|
Unlock(id string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test hook to verify that LockWithContext has attempted a lock
|
||||||
|
var postLockHook func()
|
||||||
|
|
||||||
|
// Lock the state, using the provided context for timeout and cancellation.
|
||||||
|
// This backs off slightly to an upper limit.
|
||||||
|
func LockWithContext(ctx context.Context, s State, info *LockInfo) (string, error) {
|
||||||
|
delay := time.Second
|
||||||
|
maxDelay := 16 * time.Second
|
||||||
|
for {
|
||||||
|
id, err := s.Lock(info)
|
||||||
|
if err == nil {
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
le, ok := err.(*LockError)
|
||||||
|
if !ok {
|
||||||
|
// not a lock error, so we can't retry
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if le.Info.ID == "" {
|
||||||
|
// the lock has no ID, something is wrong so don't keep trying
|
||||||
|
return "", fmt.Errorf("lock error missing ID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if postLockHook != nil {
|
||||||
|
postLockHook()
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's an existing lock, wait and try again
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// return the last lock error with the info
|
||||||
|
return "", err
|
||||||
|
case <-time.After(delay):
|
||||||
|
if delay < maxDelay {
|
||||||
|
delay *= 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a LockInfo structure, populating the required fields.
|
// Generate a LockInfo structure, populating the required fields.
|
||||||
func NewLockInfo() *LockInfo {
|
func NewLockInfo() *LockInfo {
|
||||||
// this doesn't need to be cryptographically secure, just unique.
|
// this doesn't need to be cryptographically secure, just unique.
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/logging"
|
"github.com/hashicorp/terraform/helper/logging"
|
||||||
)
|
)
|
||||||
|
@ -50,3 +52,59 @@ func TestNewLockInfo(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLockWithContext(t *testing.T) {
|
||||||
|
inmem := &InmemState{state: TestStateInitial()}
|
||||||
|
// test that it correctly wraps the inmem state
|
||||||
|
s := &inmemLocker{InmemState: inmem}
|
||||||
|
|
||||||
|
id, err := s.Lock(NewLockInfo())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a cancelled context for an immediate timeout
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
info := NewLockInfo()
|
||||||
|
info.Info = "lock with context"
|
||||||
|
_, err = LockWithContext(ctx, s, info)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("lock should have failed immediately")
|
||||||
|
}
|
||||||
|
|
||||||
|
// block until LockwithContext has made a first attempt
|
||||||
|
attempted := make(chan struct{})
|
||||||
|
postLockHook = func() {
|
||||||
|
close(attempted)
|
||||||
|
postLockHook = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlock the state during LockWithContext
|
||||||
|
unlocked := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(unlocked)
|
||||||
|
<-attempted
|
||||||
|
if err := s.Unlock(id); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
id, err = LockWithContext(ctx, s, info)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("lock should have completed within 2s:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure the goruotine completes
|
||||||
|
<-unlocked
|
||||||
|
|
||||||
|
// Lock should have been called a total of 4 times.
|
||||||
|
// 1 initial lock, 1 failure, 1 failure + 1 retry
|
||||||
|
if s.lockCounter != 4 {
|
||||||
|
t.Fatalf("lock only called %d times", s.lockCounter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,10 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
* `-backup=path` - Path to the backup file. Defaults to `-state-out` with
|
* `-backup=path` - Path to the backup file. Defaults to `-state-out` with
|
||||||
the ".backup" extension. Disabled by setting to "-".
|
the ".backup" extension. Disabled by setting to "-".
|
||||||
|
|
||||||
|
* `-lock=true` - Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
* `-lock-timeout=0s` - Duration to retry a state lock.
|
||||||
|
|
||||||
* `-input=true` - Ask for input for variables if not directly set.
|
* `-input=true` - Ask for input for variables if not directly set.
|
||||||
|
|
||||||
* `-no-color` - Disables output with coloring.
|
* `-no-color` - Disables output with coloring.
|
||||||
|
|
|
@ -42,6 +42,17 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
|
|
||||||
* `-input=true` - Whether to ask for input for provider configuration.
|
* `-input=true` - Whether to ask for input for provider configuration.
|
||||||
|
|
||||||
|
* `-lock=true` - Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
* `-lock-timeout=0s` - Duration to retry a state lock.
|
||||||
|
|
||||||
|
* `-no-color` - If specified, output won't contain any color.
|
||||||
|
|
||||||
|
* `-provider=provider` - Specified provider to use for import. This is used for
|
||||||
|
specifying provider aliases, such as "aws.eu". This defaults to the normal
|
||||||
|
provider based on the prefix of the resource being imported. You usually
|
||||||
|
don't need to specify this.
|
||||||
|
|
||||||
* `-state=path` - The path to read and save state files (unless state-out is
|
* `-state=path` - The path to read and save state files (unless state-out is
|
||||||
specified). Ignored when [remote state](/docs/state/remote.html) is used.
|
specified). Ignored when [remote state](/docs/state/remote.html) is used.
|
||||||
|
|
||||||
|
@ -49,11 +60,6 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
the state path. Ignored when [remote state](/docs/state/remote.html) is
|
the state path. Ignored when [remote state](/docs/state/remote.html) is
|
||||||
used.
|
used.
|
||||||
|
|
||||||
* `-provider=provider` - Specified provider to use for import. This is used for
|
|
||||||
specifying provider aliases, such as "aws.eu". This defaults to the normal
|
|
||||||
provider based on the prefix of the resource being imported. You usually
|
|
||||||
don't need to specify this.
|
|
||||||
|
|
||||||
* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag
|
* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag
|
||||||
can be set multiple times. Variable values are interpreted as
|
can be set multiple times. Variable values are interpreted as
|
||||||
[HCL](/docs/configuration/syntax.html#HCL), so list and map values can be
|
[HCL](/docs/configuration/syntax.html#HCL), so list and map values can be
|
||||||
|
|
|
@ -33,8 +33,8 @@ Common commands:
|
||||||
apply Builds or changes infrastructure
|
apply Builds or changes infrastructure
|
||||||
console Interactive console for Terraform interpolations
|
console Interactive console for Terraform interpolations
|
||||||
destroy Destroy Terraform-managed infrastructure
|
destroy Destroy Terraform-managed infrastructure
|
||||||
|
env Environment management
|
||||||
fmt Rewrites config files to canonical format
|
fmt Rewrites config files to canonical format
|
||||||
force-unlock Manually unlock the terraform state
|
|
||||||
get Download and install modules for the configuration
|
get Download and install modules for the configuration
|
||||||
graph Create a visual graph of Terraform resources
|
graph Create a visual graph of Terraform resources
|
||||||
import Import existing infrastructure into Terraform
|
import Import existing infrastructure into Terraform
|
||||||
|
@ -51,6 +51,7 @@ Common commands:
|
||||||
|
|
||||||
All other commands:
|
All other commands:
|
||||||
debug Debug output management (experimental)
|
debug Debug output management (experimental)
|
||||||
|
force-unlock Manually unlock the terraform state
|
||||||
state Advanced state management
|
state Advanced state management
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,15 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
* `-input=true` - Ask for input interactively if necessary. If this is false
|
* `-input=true` - Ask for input interactively if necessary. If this is false
|
||||||
and input is required, `init` will error.
|
and input is required, `init` will error.
|
||||||
|
|
||||||
|
* `-lock=true` - Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
* `-lock-timeout=0s` - Duration to retry a state lock.
|
||||||
|
|
||||||
|
* `-no-color` - If specified, output won't contain any color.
|
||||||
|
|
||||||
|
* `-force-copy` - Suppress prompts about copying state data. This is equivalent
|
||||||
|
to providing a "yes" to all confirmation prompts.
|
||||||
|
|
||||||
## Backend Config
|
## Backend Config
|
||||||
|
|
||||||
The `-backend-config` can take a path or `key=value` pair to specify additional
|
The `-backend-config` can take a path or `key=value` pair to specify additional
|
||||||
|
|
|
@ -39,6 +39,10 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
|
|
||||||
* `-input=true` - Ask for input for variables if not directly set.
|
* `-input=true` - Ask for input for variables if not directly set.
|
||||||
|
|
||||||
|
* `-lock=true` - Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
* `-lock-timeout=0s` - Duration to retry a state lock.
|
||||||
|
|
||||||
* `-module-depth=n` - Specifies the depth of modules to show in the output.
|
* `-module-depth=n` - Specifies the depth of modules to show in the output.
|
||||||
This does not affect the plan itself, only the output shown. By default,
|
This does not affect the plan itself, only the output shown. By default,
|
||||||
this is -1, which will expand all.
|
this is -1, which will expand all.
|
||||||
|
|
|
@ -31,6 +31,14 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
|
|
||||||
* `-no-color` - Disables output with coloring
|
* `-no-color` - Disables output with coloring
|
||||||
|
|
||||||
|
* `-input=true` - Ask for input for variables if not directly set.
|
||||||
|
|
||||||
|
* `-lock=true` - Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
* `-lock-timeout=0s` - Duration to retry a state lock.
|
||||||
|
|
||||||
|
* `-no-color` - If specified, output won't contain any color.
|
||||||
|
|
||||||
* `-state=path` - Path to read and write the state file to. Defaults to "terraform.tfstate".
|
* `-state=path` - Path to read and write the state file to. Defaults to "terraform.tfstate".
|
||||||
Ignored when [remote state](/docs/state/remote.html) is used.
|
Ignored when [remote state](/docs/state/remote.html) is used.
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,10 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
* `-backup=path` - Path to the backup file. Defaults to `-state-out` with
|
* `-backup=path` - Path to the backup file. Defaults to `-state-out` with
|
||||||
the ".backup" extension. Disabled by setting to "-".
|
the ".backup" extension. Disabled by setting to "-".
|
||||||
|
|
||||||
|
* `-lock=true` - Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
* `-lock-timeout=0s` - Duration to retry a state lock.
|
||||||
|
|
||||||
* `-module=path` - The module path where the resource to taint exists.
|
* `-module=path` - The module path where the resource to taint exists.
|
||||||
By default this is the root path. Other modules can be specified by
|
By default this is the root path. Other modules can be specified by
|
||||||
a period-separated list. Example: "foo" would reference the module
|
a period-separated list. Example: "foo" would reference the module
|
||||||
|
|
|
@ -47,6 +47,10 @@ certain cases, see above note). The list of available flags are:
|
||||||
time, there is a maxiumum of one tainted instance per resource, so this flag
|
time, there is a maxiumum of one tainted instance per resource, so this flag
|
||||||
can be safely omitted.
|
can be safely omitted.
|
||||||
|
|
||||||
|
* `-lock=true` - Lock the state file when locking is supported.
|
||||||
|
|
||||||
|
* `-lock-timeout=0s` - Duration to retry a state lock.
|
||||||
|
|
||||||
* `-module=path` - The module path where the resource to untaint exists.
|
* `-module=path` - The module path where the resource to untaint exists.
|
||||||
By default this is the root path. Other modules can be specified by
|
By default this is the root path. Other modules can be specified by
|
||||||
a period-separated list. Example: "foo" would reference the module
|
a period-separated list. Example: "foo" would reference the module
|
||||||
|
|
|
@ -54,8 +54,8 @@ Common commands:
|
||||||
apply Builds or changes infrastructure
|
apply Builds or changes infrastructure
|
||||||
console Interactive console for Terraform interpolations
|
console Interactive console for Terraform interpolations
|
||||||
destroy Destroy Terraform-managed infrastructure
|
destroy Destroy Terraform-managed infrastructure
|
||||||
|
env Environment management
|
||||||
fmt Rewrites config files to canonical format
|
fmt Rewrites config files to canonical format
|
||||||
force-unlock Manually unlock the terraform state
|
|
||||||
get Download and install modules for the configuration
|
get Download and install modules for the configuration
|
||||||
graph Create a visual graph of Terraform resources
|
graph Create a visual graph of Terraform resources
|
||||||
import Import existing infrastructure into Terraform
|
import Import existing infrastructure into Terraform
|
||||||
|
@ -72,6 +72,7 @@ Common commands:
|
||||||
|
|
||||||
All other commands:
|
All other commands:
|
||||||
debug Debug output management (experimental)
|
debug Debug output management (experimental)
|
||||||
|
force-unlock Manually unlock the terraform state
|
||||||
state Advanced state management
|
state Advanced state management
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue