diff --git a/command/state/state.go b/command/state/state.go new file mode 100644 index 000000000..f4c7406d3 --- /dev/null +++ b/command/state/state.go @@ -0,0 +1,83 @@ +// 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. +package message + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform/helper/slowmessage" + "github.com/hashicorp/terraform/state" + "github.com/mitchellh/cli" + "github.com/mitchellh/colorstring" +) + +const ( + LockThreshold = 250 * time.Millisecond + LockMessage = "Acquiring state lock. This may take a few moments..." + UnlockMessage = "Releasing state lock. This may take a few moments..." + + 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. +` +) + +// Lock locks the given state and outputs to the user if locking +// is taking longer than the threshold. +func Lock(s state.State, info string, ui cli.Ui, color *colorstring.Colorize) error { + sl, ok := s.(state.Locker) + if !ok { + return nil + } + + return slowmessage.Do(LockThreshold, func() error { + return sl.Lock(info) + }, func() { + if ui != nil { + ui.Output(color.Color(LockMessage)) + } + }) +} + +// Unlock unlocks the given state and outputs to the user if the +// unlock fails what can be done. +func Unlock(s state.State, ui cli.Ui, color *colorstring.Colorize) error { + sl, ok := s.(state.Locker) + if !ok { + return nil + } + + err := slowmessage.Do(LockThreshold, sl.Unlock, func() { + if ui != nil { + ui.Output(color.Color(UnlockMessage)) + } + }) + + if err != nil { + ui.Output(color.Color(fmt.Sprintf( + "\n"+strings.TrimSpace(UnlockErrorMessage)+"\n", err))) + + err = fmt.Errorf( + "Error releasing the state lock. Please see the longer error message above.") + } + + return err +}