2017-02-14 13:17:18 -06:00
|
|
|
// Package state exposes common helpers for working with state from the CLI.
|
|
|
|
//
|
|
|
|
// This is a separate package so that backends can use this for consistent
|
|
|
|
// messaging without creating a circular reference to the command package.
|
2017-04-01 13:58:19 -05:00
|
|
|
package clistate
|
2017-02-14 13:17:18 -06:00
|
|
|
|
|
|
|
import (
|
2017-04-01 13:58:19 -05:00
|
|
|
"context"
|
2017-02-14 13:17:18 -06:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
2018-02-22 19:43:21 -06:00
|
|
|
"sync"
|
2017-02-14 13:17:18 -06:00
|
|
|
"time"
|
|
|
|
|
2017-02-14 13:44:43 -06:00
|
|
|
"github.com/hashicorp/errwrap"
|
2018-02-22 19:43:21 -06:00
|
|
|
multierror "github.com/hashicorp/go-multierror"
|
2017-02-14 13:17:18 -06:00
|
|
|
"github.com/hashicorp/terraform/helper/slowmessage"
|
|
|
|
"github.com/hashicorp/terraform/state"
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
"github.com/hashicorp/terraform/states/statemgr"
|
2017-02-14 13:17:18 -06:00
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
"github.com/mitchellh/colorstring"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2017-02-14 13:44:43 -06:00
|
|
|
LockThreshold = 400 * time.Millisecond
|
|
|
|
LockMessage = "Acquiring state lock. This may take a few moments..."
|
|
|
|
LockErrorMessage = `Error acquiring the state lock: {{err}}
|
2017-02-14 13:17:18 -06:00
|
|
|
|
2017-02-14 13:44:43 -06:00
|
|
|
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..."
|
2017-02-14 13:17:18 -06:00
|
|
|
UnlockErrorMessage = `
|
|
|
|
[reset][bold][red]Error releasing the state lock![reset][red]
|
|
|
|
|
|
|
|
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
|
|
|
|
error occurred while releasing this lock. This could mean that the lock
|
|
|
|
did or did not release properly. If the lock didn't release properly,
|
|
|
|
Terraform may not be able to run future commands since it'll appear as if
|
|
|
|
the lock is held.
|
|
|
|
|
|
|
|
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.
|
|
|
|
`
|
|
|
|
)
|
|
|
|
|
2018-02-27 09:49:06 -06:00
|
|
|
// Locker allows for more convenient usage of the lower-level state.Locker
|
|
|
|
// implementations.
|
|
|
|
// The state.Locker API requires passing in a state.LockInfo struct. Locker
|
|
|
|
// implementations are expected to create the required LockInfo struct when
|
|
|
|
// Lock is called, populate the Operation field with the "reason" string
|
|
|
|
// provided, and pass that on to the underlying state.Locker.
|
|
|
|
// Locker implementations are also expected to store any state required to call
|
|
|
|
// Unlock, which is at a minimum the LockID string returned by the
|
|
|
|
// state.Locker.
|
2018-02-22 19:43:21 -06:00
|
|
|
type Locker interface {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
// Lock the provided state manager, storing the reason string in the LockInfo.
|
|
|
|
Lock(s statemgr.Locker, reason string) error
|
2018-02-22 19:43:21 -06:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
type locker struct {
|
|
|
|
mu sync.Mutex
|
|
|
|
ctx context.Context
|
|
|
|
timeout time.Duration
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
state statemgr.Locker
|
2018-02-22 19:43:21 -06:00
|
|
|
ui cli.Ui
|
|
|
|
color *colorstring.Colorize
|
|
|
|
lockID string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new Locker.
|
2018-02-27 09:49:06 -06:00
|
|
|
// 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.
|
2018-02-22 19:43:21 -06:00
|
|
|
func NewLocker(
|
|
|
|
ctx context.Context,
|
|
|
|
timeout time.Duration,
|
|
|
|
ui cli.Ui,
|
|
|
|
color *colorstring.Colorize) Locker {
|
|
|
|
|
|
|
|
l := &locker{
|
|
|
|
ctx: ctx,
|
|
|
|
timeout: timeout,
|
|
|
|
ui: ui,
|
|
|
|
color: color,
|
|
|
|
}
|
|
|
|
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.
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
func (l *locker) Lock(s statemgr.Locker, reason string) error {
|
2018-02-22 19:43:21 -06:00
|
|
|
l.mu.Lock()
|
|
|
|
defer l.mu.Unlock()
|
|
|
|
|
|
|
|
l.state = s
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(l.ctx, l.timeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
lockInfo := state.NewLockInfo()
|
|
|
|
lockInfo.Operation = reason
|
2017-02-14 17:11:55 -06:00
|
|
|
|
2017-02-14 13:44:43 -06:00
|
|
|
err := slowmessage.Do(LockThreshold, func() error {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
id, err := statemgr.LockWithContext(ctx, s, lockInfo)
|
2018-02-22 19:43:21 -06:00
|
|
|
l.lockID = id
|
2017-02-14 17:11:55 -06:00
|
|
|
return err
|
2017-02-14 13:17:18 -06:00
|
|
|
}, func() {
|
2018-02-22 19:43:21 -06:00
|
|
|
if l.ui != nil {
|
|
|
|
l.ui.Output(l.color.Color(LockMessage))
|
2017-02-14 13:17:18 -06:00
|
|
|
}
|
|
|
|
})
|
2017-02-14 13:44:43 -06:00
|
|
|
|
|
|
|
if err != nil {
|
2018-02-22 19:43:21 -06:00
|
|
|
return errwrap.Wrapf(strings.TrimSpace(LockErrorMessage), err)
|
2017-02-14 13:44:43 -06:00
|
|
|
}
|
|
|
|
|
2018-02-22 19:43:21 -06:00
|
|
|
return nil
|
2017-02-14 13:17:18 -06:00
|
|
|
}
|
|
|
|
|
2018-02-22 19:43:21 -06:00
|
|
|
func (l *locker) Unlock(parentErr error) error {
|
|
|
|
l.mu.Lock()
|
|
|
|
defer l.mu.Unlock()
|
|
|
|
|
|
|
|
if l.lockID == "" {
|
|
|
|
return parentErr
|
|
|
|
}
|
|
|
|
|
2017-02-14 17:11:55 -06:00
|
|
|
err := slowmessage.Do(LockThreshold, func() error {
|
2018-02-22 19:43:21 -06:00
|
|
|
return l.state.Unlock(l.lockID)
|
2017-02-14 17:11:55 -06:00
|
|
|
}, func() {
|
2018-02-22 19:43:21 -06:00
|
|
|
if l.ui != nil {
|
|
|
|
l.ui.Output(l.color.Color(UnlockMessage))
|
2017-02-14 13:17:18 -06:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
2018-02-22 19:43:21 -06:00
|
|
|
l.ui.Output(l.color.Color(fmt.Sprintf(
|
2017-02-14 13:17:18 -06:00
|
|
|
"\n"+strings.TrimSpace(UnlockErrorMessage)+"\n", err)))
|
|
|
|
|
2018-02-22 19:43:21 -06:00
|
|
|
if parentErr != nil {
|
|
|
|
parentErr = multierror.Append(parentErr, err)
|
|
|
|
}
|
2017-02-14 13:17:18 -06:00
|
|
|
}
|
|
|
|
|
2018-02-22 19:43:21 -06:00
|
|
|
return parentErr
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
type noopLocker struct{}
|
|
|
|
|
|
|
|
// NewNoopLocker returns a valid Locker that does nothing.
|
|
|
|
func NewNoopLocker() Locker {
|
|
|
|
return noopLocker{}
|
|
|
|
}
|
|
|
|
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
func (l noopLocker) Lock(statemgr.Locker, string) error {
|
2018-02-22 19:43:21 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l noopLocker) Unlock(err error) error {
|
2017-02-14 13:17:18 -06:00
|
|
|
return err
|
|
|
|
}
|