2015-02-21 20:52:55 +01:00
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
2017-02-14 23:43:42 +01:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2015-02-21 20:52:55 +01:00
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
)
|
|
|
|
|
2015-02-22 01:04:32 +01:00
|
|
|
// State is the collection of all state interfaces.
|
|
|
|
type State interface {
|
|
|
|
StateReader
|
|
|
|
StateWriter
|
|
|
|
StateRefresher
|
|
|
|
StatePersister
|
|
|
|
}
|
|
|
|
|
2015-02-21 20:52:55 +01:00
|
|
|
// StateReader is the interface for things that can return a state. Retrieving
|
|
|
|
// the state here must not error. Loading the state fresh (an operation that
|
|
|
|
// can likely error) should be implemented by RefreshState. If a state hasn't
|
|
|
|
// been loaded yet, it is okay for State to return nil.
|
|
|
|
type StateReader interface {
|
|
|
|
State() *terraform.State
|
|
|
|
}
|
|
|
|
|
|
|
|
// StateWriter is the interface that must be implemented by something that
|
|
|
|
// can write a state. Writing the state can be cached or in-memory, as
|
|
|
|
// full persistence should be implemented by StatePersister.
|
|
|
|
type StateWriter interface {
|
|
|
|
WriteState(*terraform.State) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// StateRefresher is the interface that is implemented by something that
|
|
|
|
// can load a state. This might be refreshing it from a remote location or
|
|
|
|
// it might simply be reloading it from disk.
|
|
|
|
type StateRefresher interface {
|
|
|
|
RefreshState() error
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatePersister is implemented to truly persist a state. Whereas StateWriter
|
|
|
|
// is allowed to perhaps be caching in memory, PersistState must write the
|
|
|
|
// state to some durable storage.
|
|
|
|
type StatePersister interface {
|
|
|
|
PersistState() error
|
|
|
|
}
|
2017-01-10 00:00:05 +01:00
|
|
|
|
|
|
|
// Locker is implemented to lock state during command execution.
|
2017-02-14 23:43:42 +01:00
|
|
|
// The info parameter can be recorded with the lock, but the
|
|
|
|
// implementation should not depend in its value. The string returned by Lock
|
|
|
|
// is an ID corresponding to the lock acquired, and must be passed to Unlock to
|
|
|
|
// ensure that the correct lock is being released.
|
|
|
|
//
|
|
|
|
// Lock and Unlock may return an error value of type LockError which in turn
|
|
|
|
// can contain the LockInfo of a conflicting lock.
|
2017-01-10 00:00:05 +01:00
|
|
|
type Locker interface {
|
2017-02-14 23:43:42 +01:00
|
|
|
Lock(info *LockInfo) (string, error)
|
|
|
|
Unlock(id string) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// LockInfo stores metadata for locks taken.
|
|
|
|
type LockInfo struct {
|
|
|
|
ID string // unique ID
|
|
|
|
Path string // Path to the state file
|
|
|
|
Created time.Time // The time the lock was taken
|
|
|
|
Version string // Terraform version
|
|
|
|
Operation string // Terraform operation
|
|
|
|
Who string // user@hostname when available
|
|
|
|
Info string // Extra info field
|
|
|
|
}
|
|
|
|
|
|
|
|
// Err returns the lock info formatted in an error
|
|
|
|
func (l *LockInfo) Err() error {
|
|
|
|
return fmt.Errorf("state locked. path:%q, created:%s, info:%q",
|
|
|
|
l.Path, l.Created, l.Info)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *LockInfo) String() string {
|
|
|
|
js, err := json.Marshal(l)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return string(js)
|
|
|
|
}
|
|
|
|
|
|
|
|
type LockError struct {
|
|
|
|
Info *LockInfo
|
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *LockError) Error() string {
|
|
|
|
var out []string
|
|
|
|
if e.Err != nil {
|
|
|
|
out = append(out, e.Err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Info != nil {
|
|
|
|
out = append(out, e.Info.Err().Error())
|
|
|
|
}
|
|
|
|
return strings.Join(out, "\n")
|
2017-01-10 00:00:05 +01:00
|
|
|
}
|