terraform/terraform/state.go

1541 lines
37 KiB
Go
Raw Normal View History

package terraform
import (
"bufio"
2014-09-16 23:26:27 +02:00
"bytes"
"encoding/json"
2014-06-19 01:38:08 +02:00
"fmt"
"io"
"io/ioutil"
2014-09-16 21:34:08 +02:00
"reflect"
2014-09-16 23:26:27 +02:00
"sort"
"strconv"
"strings"
2014-09-16 21:12:15 +02:00
"github.com/hashicorp/go-version"
2014-09-16 21:12:15 +02:00
"github.com/hashicorp/terraform/config"
"github.com/mitchellh/copystructure"
)
2014-09-16 23:26:27 +02:00
const (
2014-10-02 22:45:34 +02:00
// StateVersion is the current version for our state file
StateVersion = 2
2014-09-16 23:26:27 +02:00
)
// rootModulePath is the path of the root module
var rootModulePath = []string{"root"}
// normalizeModulePath takes a raw module path and returns a path that
// has the rootModulePath prepended to it. If I could go back in time I
// would've never had a rootModulePath (empty path would be root). We can
// still fix this but thats a big refactor that my branch doesn't make sense
// for. Instead, this function normalizes paths.
func normalizeModulePath(p []string) []string {
k := len(rootModulePath)
// If we already have a root module prefix, we're done
if len(p) >= len(rootModulePath) {
if reflect.DeepEqual(p[:k], rootModulePath) {
return p
}
}
// None? Prefix it
result := make([]string, len(rootModulePath)+len(p))
copy(result, rootModulePath)
copy(result[k:], p)
return result
}
// State keeps track of a snapshot state-of-the-world that Terraform
// can use to keep track of what real world resources it is actually
2014-09-16 00:31:17 +02:00
// managing. This is the latest format as of Terraform 0.3
type State struct {
2014-09-16 00:31:17 +02:00
// Version is the protocol version. Currently only "1".
Version int `json:"version"`
// TFVersion is the version of Terraform that wrote this state.
TFVersion string `json:"terraform_version,omitempty"`
2014-09-16 00:31:17 +02:00
// Serial is incremented on any operation that modifies
// the State file. It is used to detect potentially conflicting
// updates.
Serial int64 `json:"serial"`
// Remote is used to track the metadata required to
// pull and push state files from a remote storage endpoint.
Remote *RemoteState `json:"remote,omitempty"`
2014-09-16 00:31:17 +02:00
// Modules contains all the modules in a breadth-first order
Modules []*ModuleState `json:"modules"`
}
2014-10-01 00:39:24 +02:00
// NewState is used to initialize a blank state
func NewState() *State {
s := &State{}
s.init()
return s
}
2014-09-23 23:57:17 +02:00
// Children returns the ModuleStates that are direct children of
// the given path. If the path is "root", for example, then children
// returned might be "root.child", but not "root.child.grandchild".
func (s *State) Children(path []string) []*ModuleState {
// TODO: test
result := make([]*ModuleState, 0)
for _, m := range s.Modules {
if len(m.Path) != len(path)+1 {
continue
}
if !reflect.DeepEqual(path, m.Path[:len(path)]) {
continue
}
result = append(result, m)
}
return result
}
2014-09-23 23:15:40 +02:00
// AddModule adds the module with the given path to the state.
//
// This should be the preferred method to add module states since it
// allows us to optimize lookups later as well as control sorting.
2014-09-23 23:20:26 +02:00
func (s *State) AddModule(path []string) *ModuleState {
2014-09-23 23:15:40 +02:00
m := &ModuleState{Path: path}
m.init()
s.Modules = append(s.Modules, m)
s.sort()
2014-09-23 23:15:40 +02:00
return m
}
// ModuleByPath is used to lookup the module state for the given path.
2015-09-11 20:56:20 +02:00
// This should be the preferred lookup mechanism as it allows for future
// lookup optimizations.
func (s *State) ModuleByPath(path []string) *ModuleState {
2014-09-17 01:21:09 +02:00
if s == nil {
return nil
}
for _, mod := range s.Modules {
2014-09-17 01:55:10 +02:00
if mod.Path == nil {
panic("missing module path")
}
2014-09-16 21:34:08 +02:00
if reflect.DeepEqual(mod.Path, path) {
return mod
}
}
return nil
}
2015-01-27 19:13:50 +01:00
// ModuleOrphans returns all the module orphans in this state by
// returning their full paths. These paths can be used with ModuleByPath
// to return the actual state.
func (s *State) ModuleOrphans(path []string, c *config.Config) [][]string {
2015-07-19 23:14:05 +02:00
// direct keeps track of what direct children we have both in our config
// and in our state. childrenKeys keeps track of what isn't an orphan.
direct := make(map[string]struct{})
2015-01-27 19:13:50 +01:00
childrenKeys := make(map[string]struct{})
if c != nil {
for _, m := range c.Modules {
childrenKeys[m.Name] = struct{}{}
2015-07-19 23:14:05 +02:00
direct[m.Name] = struct{}{}
}
2015-01-27 19:13:50 +01:00
}
// Go over the direct children and find any that aren't in our keys.
2015-01-27 19:13:50 +01:00
var orphans [][]string
for _, m := range s.Children(path) {
key := m.Path[len(m.Path)-1]
2015-01-27 19:13:50 +01:00
// Record that we found this key as a direct child. We use this
// later to find orphan nested modules.
direct[key] = struct{}{}
2015-07-19 23:14:05 +02:00
// If we have a direct child still in our config, it is not an orphan
if _, ok := childrenKeys[key]; ok {
continue
}
2015-01-27 19:13:50 +01:00
orphans = append(orphans, m.Path)
}
// Find the orphans that are nested...
for _, m := range s.Modules {
// We only want modules that are at least grandchildren
if len(m.Path) < len(path)+2 {
continue
}
// If it isn't part of our tree, continue
if !reflect.DeepEqual(path, m.Path[:len(path)]) {
continue
}
// If we have the direct child, then just skip it.
key := m.Path[len(path)]
if _, ok := direct[key]; ok {
continue
}
orphanPath := m.Path[:len(path)+1]
// Don't double-add if we've already added this orphan (which can happen if
// there are multiple nested sub-modules that get orphaned together).
alreadyAdded := false
for _, o := range orphans {
if reflect.DeepEqual(o, orphanPath) {
alreadyAdded = true
break
}
}
if alreadyAdded {
continue
}
// Add this orphan
orphans = append(orphans, orphanPath)
}
2015-01-27 19:13:50 +01:00
return orphans
}
2015-02-22 19:35:26 +01:00
// Empty returns true if the state is empty.
func (s *State) Empty() bool {
if s == nil {
return true
}
return len(s.Modules) == 0
}
2015-02-22 19:29:42 +01:00
// IsRemote returns true if State represents a state that exists and is
// remote.
func (s *State) IsRemote() bool {
if s == nil {
return false
}
if s.Remote == nil {
return false
}
if s.Remote.Type == "" {
return false
}
return true
}
// Remove removes the item in the state at the given address, returning
// any errors that may have occurred.
//
// If the address references a module state or resource, it will delete
// all children as well. To check what will be deleted, use a StateFilter
// first.
func (s *State) Remove(addr ...string) error {
// Filter out what we need to delete
filter := &StateFilter{State: s}
results, err := filter.Filter(addr...)
if err != nil {
return err
}
// If we have no results, just exit early, we're not going to do anything.
// While what happens below is fairly fast, this is an important early
// exit since the prune below might modify the state more and we don't
// want to modify the state if we don't have to.
if len(results) == 0 {
return nil
}
// Go through each result and grab what we need
removed := make(map[interface{}]struct{})
for _, r := range results {
// Convert the path to our own type
path := append([]string{"root"}, r.Path...)
// If we removed this already, then ignore
if _, ok := removed[r.Value]; ok {
continue
}
// If we removed the parent already, then ignore
if r.Parent != nil {
if _, ok := removed[r.Parent.Value]; ok {
continue
}
}
// Add this to the removed list
removed[r.Value] = struct{}{}
switch v := r.Value.(type) {
case *ModuleState:
s.removeModule(path, v)
case *ResourceState:
s.removeResource(path, v)
case *InstanceState:
s.removeInstance(path, r.Parent.Value.(*ResourceState), v)
default:
return fmt.Errorf("unknown type to delete: %T", r.Value)
}
}
// Prune since the removal functions often do the bare minimum to
// remove a thing and may leave around dangling empty modules, resources,
// etc. Prune will clean that all up.
s.prune()
return nil
}
func (s *State) removeModule(path []string, v *ModuleState) {
for i, m := range s.Modules {
if m == v {
s.Modules, s.Modules[len(s.Modules)-1] = append(s.Modules[:i], s.Modules[i+1:]...), nil
return
}
}
}
func (s *State) removeResource(path []string, v *ResourceState) {
// Get the module this resource lives in. If it doesn't exist, we're done.
mod := s.ModuleByPath(path)
if mod == nil {
return
}
// Find this resource. This is a O(N) lookup when if we had the key
// it could be O(1) but even with thousands of resources this shouldn't
// matter right now. We can easily up performance here when the time comes.
for k, r := range mod.Resources {
if r == v {
// Found it
delete(mod.Resources, k)
return
}
}
}
func (s *State) removeInstance(path []string, r *ResourceState, v *InstanceState) {
// Go through the resource and find the instance that matches this
// (if any) and remove it.
// Check primary
if r.Primary == v {
r.Primary = nil
return
}
// Check lists
2016-04-21 21:59:10 +02:00
lists := [][]*InstanceState{r.Deposed}
for _, is := range lists {
for i, instance := range is {
if instance == v {
// Found it, remove it
is, is[len(is)-1] = append(is[:i], is[i+1:]...), nil
// Done
return
}
}
}
}
// RootModule returns the ModuleState for the root module
func (s *State) RootModule() *ModuleState {
2014-09-17 00:09:40 +02:00
root := s.ModuleByPath(rootModulePath)
if root == nil {
panic("missing root module")
}
return root
}
2015-02-20 22:39:49 +01:00
// Equal tests if one state is equal to another.
func (s *State) Equal(other *State) bool {
2015-02-24 06:26:33 +01:00
// If one is nil, we do a direct check
if s == nil || other == nil {
return s == other
}
2015-02-20 22:39:49 +01:00
// If the versions are different, they're certainly not equal
if s.Version != other.Version {
return false
}
// If any of the modules are not equal, then this state isn't equal
2015-02-24 06:43:54 +01:00
if len(s.Modules) != len(other.Modules) {
return false
}
2015-02-20 22:39:49 +01:00
for _, m := range s.Modules {
// This isn't very optimal currently but works.
otherM := other.ModuleByPath(m.Path)
if otherM == nil {
return false
}
// If they're not equal, then we're not equal!
if !m.Equal(otherM) {
return false
}
}
return true
}
2015-02-24 06:32:27 +01:00
// DeepCopy performs a deep copy of the state structure and returns
// a new structure.
func (s *State) DeepCopy() *State {
if s == nil {
return nil
}
n := &State{
Version: s.Version,
TFVersion: s.TFVersion,
Serial: s.Serial,
Modules: make([]*ModuleState, 0, len(s.Modules)),
2015-02-24 06:32:27 +01:00
}
for _, mod := range s.Modules {
n.Modules = append(n.Modules, mod.deepcopy())
}
if s.Remote != nil {
n.Remote = s.Remote.deepcopy()
}
return n
}
2015-02-24 06:26:33 +01:00
// IncrementSerialMaybe increments the serial number of this state
// if it different from the other state.
func (s *State) IncrementSerialMaybe(other *State) {
if s == nil {
return
}
if other == nil {
return
}
if s.Serial > other.Serial {
return
}
if other.TFVersion != s.TFVersion || !s.Equal(other) {
if other.Serial > s.Serial {
s.Serial = other.Serial
}
2015-02-24 06:26:33 +01:00
s.Serial++
}
}
// FromFutureTerraform checks if this state was written by a Terraform
// version from the future.
func (s *State) FromFutureTerraform() bool {
// No TF version means it is certainly from the past
if s.TFVersion == "" {
return false
}
v := version.Must(version.NewVersion(s.TFVersion))
return SemVersion.LessThan(v)
}
2014-09-16 23:26:27 +02:00
func (s *State) init() {
if s.Version == 0 {
2014-10-02 22:45:34 +02:00
s.Version = StateVersion
2014-09-16 23:26:27 +02:00
}
if s.ModuleByPath(rootModulePath) == nil {
s.AddModule(rootModulePath)
2014-09-16 23:26:27 +02:00
}
}
2014-09-16 02:06:32 +02:00
// prune is used to remove any resources that are no longer required
func (s *State) prune() {
if s == nil {
return
}
2014-09-16 21:34:08 +02:00
for _, mod := range s.Modules {
2014-09-16 02:06:32 +02:00
mod.prune()
}
if s.Remote != nil && s.Remote.Empty() {
s.Remote = nil
}
2014-09-16 02:06:32 +02:00
}
// sort sorts the modules
func (s *State) sort() {
sort.Sort(moduleStateSort(s.Modules))
// Allow modules to be sorted
for _, m := range s.Modules {
m.sort()
}
}
2014-09-16 21:34:08 +02:00
func (s *State) GoString() string {
return fmt.Sprintf("*%#v", *s)
}
2014-09-16 23:26:27 +02:00
func (s *State) String() string {
2015-02-26 19:29:23 +01:00
if s == nil {
return "<nil>"
}
2014-09-16 23:26:27 +02:00
var buf bytes.Buffer
for _, m := range s.Modules {
mStr := m.String()
2014-09-16 23:26:27 +02:00
// If we're the root module, we just write the output directly.
if reflect.DeepEqual(m.Path, rootModulePath) {
2014-09-23 23:20:26 +02:00
buf.WriteString(mStr + "\n")
continue
2014-09-16 23:26:27 +02:00
}
buf.WriteString(fmt.Sprintf("module.%s:\n", strings.Join(m.Path[1:], ".")))
2014-09-16 23:26:27 +02:00
s := bufio.NewScanner(strings.NewReader(mStr))
for s.Scan() {
text := s.Text()
if text != "" {
text = " " + text
}
buf.WriteString(fmt.Sprintf("%s\n", text))
2014-09-16 23:26:27 +02:00
}
}
return strings.TrimSpace(buf.String())
2014-09-16 23:26:27 +02:00
}
// RemoteState is used to track the information about a remote
// state store that we push/pull state to.
type RemoteState struct {
// Type controls the client we use for the remote state
Type string `json:"type"`
// Config is used to store arbitrary configuration that
// is type specific
Config map[string]string `json:"config"`
}
func (r *RemoteState) deepcopy() *RemoteState {
confCopy := make(map[string]string, len(r.Config))
for k, v := range r.Config {
confCopy[k] = v
}
return &RemoteState{
Type: r.Type,
Config: confCopy,
}
}
func (r *RemoteState) Empty() bool {
2015-02-22 19:35:26 +01:00
return r == nil || r.Type == ""
}
2014-10-01 00:39:24 +02:00
func (r *RemoteState) Equals(other *RemoteState) bool {
if r.Type != other.Type {
2014-10-01 00:39:24 +02:00
return false
}
if len(r.Config) != len(other.Config) {
2014-10-01 00:39:24 +02:00
return false
}
for k, v := range r.Config {
if other.Config[k] != v {
return false
}
2014-10-01 00:39:24 +02:00
}
return true
}
func (r *RemoteState) GoString() string {
return fmt.Sprintf("*%#v", *r)
}
// OutputState is used to track the state relevant to a single output.
type OutputState struct {
// Sensitive describes whether the output is considered sensitive,
// which may lead to masking the value on screen in some cases.
Sensitive bool `json:"sensitive"`
// Type describes the structure of Value. Valid values are "string",
// "map" and "list"
Type string `json:"type"`
// Value contains the value of the output, in the structure described
// by the Type field.
Value interface{} `json:"value"`
}
func (s *OutputState) String() string {
// This is a v0.6.x implementation only
return fmt.Sprintf("%s", s.Value.(string))
}
// Equal compares two OutputState structures for equality. nil values are
// considered equal.
func (s *OutputState) Equal(other *OutputState) bool {
if s == nil && other == nil {
return true
}
if s == nil || other == nil {
return false
}
if s.Type != other.Type {
return false
}
if s.Sensitive != other.Sensitive {
return false
}
if !reflect.DeepEqual(s.Value, other.Value) {
return false
}
return true
}
func (s *OutputState) deepcopy() *OutputState {
if s == nil {
return nil
}
valueCopy, err := copystructure.Copy(s.Value)
if err != nil {
panic(fmt.Errorf("Error copying output value: %s", err))
}
n := &OutputState{
Type: s.Type,
Sensitive: s.Sensitive,
Value: valueCopy,
}
return n
}
2014-09-16 00:31:17 +02:00
// ModuleState is used to track all the state relevant to a single
// module. Previous to Terraform 0.3, all state belonged to the "root"
// module.
type ModuleState struct {
// Path is the import path from the root module. Modules imports are
// always disjoint, so the path represents amodule tree
Path []string `json:"path"`
// Outputs declared by the module and maintained for each module
// even though only the root module technically needs to be kept.
// This allows operators to inspect values at the boundaries.
Outputs map[string]*OutputState `json:"outputs"`
2014-09-16 00:31:17 +02:00
// Resources is a mapping of the logically named resource to
// the state of the resource. Each resource may actually have
// N instances underneath, although a user only needs to think
// about the 1:1 case.
Resources map[string]*ResourceState `json:"resources"`
// Dependencies are a list of things that this module relies on
// existing to remain intact. For example: an module may depend
// on a VPC ID given by an aws_vpc resource.
//
// Terraform uses this information to build valid destruction
// orders and to warn the user if they're destroying a module that
// another resource depends on.
//
// Things can be put into this list that may not be managed by
// Terraform. If Terraform doesn't find a matching ID in the
// overall state, then it assumes it isn't managed and doesn't
// worry about it.
Dependencies []string `json:"depends_on,omitempty"`
}
2015-02-20 22:39:49 +01:00
// Equal tests whether one module state is equal to another.
func (m *ModuleState) Equal(other *ModuleState) bool {
// Paths must be equal
if !reflect.DeepEqual(m.Path, other.Path) {
return false
}
// Outputs must be equal
if len(m.Outputs) != len(other.Outputs) {
return false
}
for k, v := range m.Outputs {
if !other.Outputs[k].Equal(v) {
2015-02-20 22:39:49 +01:00
return false
}
}
// Dependencies must be equal. This sorts these in place but
// this shouldn't cause any problems.
sort.Strings(m.Dependencies)
sort.Strings(other.Dependencies)
if len(m.Dependencies) != len(other.Dependencies) {
return false
}
for i, d := range m.Dependencies {
if other.Dependencies[i] != d {
return false
}
}
// Resources must be equal
if len(m.Resources) != len(other.Resources) {
return false
}
for k, r := range m.Resources {
otherR, ok := other.Resources[k]
if !ok {
return false
}
if !r.Equal(otherR) {
return false
}
}
return true
}
// IsRoot says whether or not this module diff is for the root module.
func (m *ModuleState) IsRoot() bool {
return reflect.DeepEqual(m.Path, rootModulePath)
}
2014-09-16 21:34:08 +02:00
// Orphans returns a list of keys of resources that are in the State
// but aren't present in the configuration itself. Hence, these keys
// represent the state of resources that are orphans.
func (m *ModuleState) Orphans(c *config.Config) []string {
keys := make(map[string]struct{})
for k, _ := range m.Resources {
keys[k] = struct{}{}
}
if c != nil {
for _, r := range c.Resources {
delete(keys, r.Id())
2014-09-16 21:34:08 +02:00
for k, _ := range keys {
if strings.HasPrefix(k, r.Id()+".") {
delete(keys, k)
}
2014-10-02 20:34:08 +02:00
}
2014-09-16 21:34:08 +02:00
}
}
result := make([]string, 0, len(keys))
for k, _ := range keys {
result = append(result, k)
}
return result
}
// View returns a view with the given resource prefix.
func (m *ModuleState) View(id string) *ModuleState {
if m == nil {
return m
}
r := m.deepcopy()
for k, _ := range r.Resources {
if id == k || strings.HasPrefix(k, id+".") {
continue
}
delete(r.Resources, k)
}
return r
}
2014-09-16 21:12:15 +02:00
func (m *ModuleState) init() {
if m.Outputs == nil {
m.Outputs = make(map[string]*OutputState)
2014-09-16 21:12:15 +02:00
}
if m.Resources == nil {
m.Resources = make(map[string]*ResourceState)
}
}
func (m *ModuleState) deepcopy() *ModuleState {
2014-09-16 21:34:08 +02:00
if m == nil {
return nil
}
n := &ModuleState{
Path: make([]string, len(m.Path)),
Outputs: make(map[string]*OutputState, len(m.Outputs)),
Resources: make(map[string]*ResourceState, len(m.Resources)),
Dependencies: make([]string, len(m.Dependencies)),
}
copy(n.Path, m.Path)
copy(n.Dependencies, m.Dependencies)
for k, v := range m.Outputs {
n.Outputs[k] = v.deepcopy()
}
for k, v := range m.Resources {
n.Resources[k] = v.deepcopy()
}
return n
}
2014-09-16 02:06:32 +02:00
// prune is used to remove any resources that are no longer required
func (m *ModuleState) prune() {
for k, v := range m.Resources {
v.prune()
if (v.Primary == nil || v.Primary.ID == "") && len(v.Deposed) == 0 {
2014-09-16 02:06:32 +02:00
delete(m.Resources, k)
}
}
for k, v := range m.Outputs {
if v.Value == config.UnknownVariableValue {
delete(m.Outputs, k)
}
}
2014-09-16 02:06:32 +02:00
}
func (m *ModuleState) sort() {
for _, v := range m.Resources {
v.sort()
}
}
2014-09-16 21:34:08 +02:00
func (m *ModuleState) GoString() string {
return fmt.Sprintf("*%#v", *m)
}
func (m *ModuleState) String() string {
var buf bytes.Buffer
if len(m.Resources) == 0 {
buf.WriteString("<no state>")
}
names := make([]string, 0, len(m.Resources))
for name, _ := range m.Resources {
names = append(names, name)
}
sort.Strings(names)
for _, k := range names {
rs := m.Resources[k]
var id string
if rs.Primary != nil {
id = rs.Primary.ID
}
if id == "" {
id = "<not created>"
}
taintStr := ""
if rs.Primary.Tainted {
taintStr = " (tainted)"
}
deposedStr := ""
if len(rs.Deposed) > 0 {
deposedStr = fmt.Sprintf(" (%d deposed)", len(rs.Deposed))
}
buf.WriteString(fmt.Sprintf("%s:%s%s\n", k, taintStr, deposedStr))
buf.WriteString(fmt.Sprintf(" ID = %s\n", id))
if rs.Provider != "" {
buf.WriteString(fmt.Sprintf(" provider = %s\n", rs.Provider))
}
var attributes map[string]string
if rs.Primary != nil {
attributes = rs.Primary.Attributes
}
attrKeys := make([]string, 0, len(attributes))
for ak, _ := range attributes {
if ak == "id" {
continue
}
attrKeys = append(attrKeys, ak)
}
sort.Strings(attrKeys)
for _, ak := range attrKeys {
av := attributes[ak]
buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av))
}
for idx, t := range rs.Deposed {
2016-04-21 21:59:10 +02:00
taintStr := ""
if t.Tainted {
taintStr = " (tainted)"
}
buf.WriteString(fmt.Sprintf(" Deposed ID %d = %s%s\n", idx+1, t.ID, taintStr))
}
if len(rs.Dependencies) > 0 {
buf.WriteString(fmt.Sprintf("\n Dependencies:\n"))
for _, dep := range rs.Dependencies {
buf.WriteString(fmt.Sprintf(" %s\n", dep))
}
}
}
if len(m.Outputs) > 0 {
buf.WriteString("\nOutputs:\n\n")
ks := make([]string, 0, len(m.Outputs))
for k, _ := range m.Outputs {
ks = append(ks, k)
}
sort.Strings(ks)
for _, k := range ks {
v := m.Outputs[k]
switch vTyped := v.Value.(type) {
case string:
buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped))
case []interface{}:
buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped))
case map[string]interface{}:
var mapKeys []string
for key, _ := range vTyped {
mapKeys = append(mapKeys, key)
}
sort.Strings(mapKeys)
var mapBuf bytes.Buffer
mapBuf.WriteString("{")
for _, key := range mapKeys {
mapBuf.WriteString(fmt.Sprintf("%s:%s ", key, vTyped[key]))
}
mapBuf.WriteString("}")
buf.WriteString(fmt.Sprintf("%s = %s\n", k, mapBuf.String()))
}
}
}
return buf.String()
}
// ResourceStateKey is a structured representation of the key used for the
// ModuleState.Resources mapping
type ResourceStateKey struct {
Name string
Type string
Mode config.ResourceMode
Index int
}
// Equal determines whether two ResourceStateKeys are the same
func (rsk *ResourceStateKey) Equal(other *ResourceStateKey) bool {
if rsk == nil || other == nil {
return false
}
if rsk.Mode != other.Mode {
return false
}
if rsk.Type != other.Type {
return false
}
if rsk.Name != other.Name {
return false
}
if rsk.Index != other.Index {
return false
}
return true
}
func (rsk *ResourceStateKey) String() string {
if rsk == nil {
return ""
}
var prefix string
switch rsk.Mode {
case config.ManagedResourceMode:
prefix = ""
case config.DataResourceMode:
prefix = "data."
default:
panic(fmt.Errorf("unknown resource mode %s", rsk.Mode))
}
if rsk.Index == -1 {
return fmt.Sprintf("%s%s.%s", prefix, rsk.Type, rsk.Name)
}
return fmt.Sprintf("%s%s.%s.%d", prefix, rsk.Type, rsk.Name, rsk.Index)
}
// ParseResourceStateKey accepts a key in the format used by
// ModuleState.Resources and returns a resource name and resource index. In the
// state, a resource has the format "type.name.index" or "type.name". In the
// latter case, the index is returned as -1.
func ParseResourceStateKey(k string) (*ResourceStateKey, error) {
parts := strings.Split(k, ".")
mode := config.ManagedResourceMode
if len(parts) > 0 && parts[0] == "data" {
mode = config.DataResourceMode
// Don't need the constant "data" prefix for parsing
// now that we've figured out the mode.
parts = parts[1:]
}
if len(parts) < 2 || len(parts) > 3 {
return nil, fmt.Errorf("Malformed resource state key: %s", k)
}
rsk := &ResourceStateKey{
Mode: mode,
Type: parts[0],
Name: parts[1],
Index: -1,
}
if len(parts) == 3 {
index, err := strconv.Atoi(parts[2])
if err != nil {
return nil, fmt.Errorf("Malformed resource state key index: %s", k)
}
rsk.Index = index
}
return rsk, nil
}
2014-09-16 00:31:17 +02:00
// ResourceState holds the state of a resource that is used so that
// a provider can find and manage an existing resource as well as for
// storing attributes that are used to populate variables of child
// resources.
//
// Attributes has attributes about the created resource that are
// queryable in interpolation: "${type.id.attr}"
//
// Extra is just extra data that a provider can return that we store
// for later, but is not exposed in any way to the user.
//
type ResourceState struct {
// This is filled in and managed by Terraform, and is the resource
// type itself such as "mycloud_instance". If a resource provider sets
// this value, it won't be persisted.
Type string `json:"type"`
2014-09-16 00:31:17 +02:00
// Dependencies are a list of things that this resource relies on
// existing to remain intact. For example: an AWS instance might
// depend on a subnet (which itself might depend on a VPC, and so
// on).
//
// Terraform uses this information to build valid destruction
// orders and to warn the user if they're destroying a resource that
// another resource depends on.
//
// Things can be put into this list that may not be managed by
// Terraform. If Terraform doesn't find a matching ID in the
// overall state, then it assumes it isn't managed and doesn't
// worry about it.
Dependencies []string `json:"depends_on,omitempty"`
// Primary is the current active instance for this resource.
// It can be replaced but only after a successful creation.
// This is the instances on which providers will act.
Primary *InstanceState `json:"primary"`
// Deposed is used in the mechanics of CreateBeforeDestroy: the existing
// Primary is Deposed to get it out of the way for the replacement Primary to
// be created by Apply. If the replacement Primary creates successfully, the
2016-04-21 21:59:10 +02:00
// Deposed instance is cleaned up.
//
// If there were problems creating the replacement Primary, the Deposed
// instance and the (now tainted) replacement Primary will be swapped so the
// tainted replacement will be cleaned up instead.
//
// An instance will remain in the Deposed list until it is successfully
// destroyed and purged.
Deposed []*InstanceState `json:"deposed,omitempty"`
// Provider is used when a resource is connected to a provider with an alias.
// If this string is empty, the resource is connected to the default provider,
// e.g. "aws_instance" goes with the "aws" provider.
// If the resource block contained a "provider" key, that value will be set here.
Provider string `json:"provider,omitempty"`
2014-09-16 02:30:18 +02:00
}
2015-02-20 22:39:49 +01:00
// Equal tests whether two ResourceStates are equal.
func (s *ResourceState) Equal(other *ResourceState) bool {
if s.Type != other.Type {
return false
}
if s.Provider != other.Provider {
return false
}
2015-02-20 22:39:49 +01:00
// Dependencies must be equal
sort.Strings(s.Dependencies)
sort.Strings(other.Dependencies)
if len(s.Dependencies) != len(other.Dependencies) {
return false
}
for i, d := range s.Dependencies {
if other.Dependencies[i] != d {
return false
}
}
// States must be equal
if !s.Primary.Equal(other.Primary) {
return false
}
return true
}
// Taint marks a resource as tainted.
2015-02-26 18:58:56 +01:00
func (r *ResourceState) Taint() {
2016-04-21 21:59:10 +02:00
if r.Primary != nil {
r.Primary.Tainted = true
}
2015-02-26 18:58:56 +01:00
}
// Untaint unmarks a resource as tainted.
func (r *ResourceState) Untaint() {
2016-04-21 21:59:10 +02:00
if r.Primary != nil {
r.Primary.Tainted = false
}
}
2014-09-16 21:12:15 +02:00
func (r *ResourceState) init() {
2014-09-16 21:34:08 +02:00
if r.Primary == nil {
r.Primary = &InstanceState{}
2014-09-16 21:12:15 +02:00
}
2014-09-17 01:21:09 +02:00
r.Primary.init()
2014-09-16 21:12:15 +02:00
}
func (r *ResourceState) deepcopy() *ResourceState {
2014-09-16 21:34:08 +02:00
if r == nil {
return nil
}
2015-02-26 18:58:56 +01:00
n := &ResourceState{
Type: r.Type,
2015-02-26 18:58:56 +01:00
Dependencies: nil,
Primary: r.Primary.DeepCopy(),
Provider: r.Provider,
}
2015-02-26 18:58:56 +01:00
if r.Dependencies != nil {
n.Dependencies = make([]string, len(r.Dependencies))
copy(n.Dependencies, r.Dependencies)
}
if r.Deposed != nil {
n.Deposed = make([]*InstanceState, 0, len(r.Deposed))
for _, inst := range r.Deposed {
n.Deposed = append(n.Deposed, inst.DeepCopy())
}
}
2015-02-26 18:58:56 +01:00
return n
}
2014-09-16 02:06:32 +02:00
// prune is used to remove any instances that are no longer required
func (r *ResourceState) prune() {
n := len(r.Deposed)
for i := 0; i < n; i++ {
inst := r.Deposed[i]
if inst == nil || inst.ID == "" {
copy(r.Deposed[i:], r.Deposed[i+1:])
r.Deposed[n-1] = nil
n--
i--
}
}
r.Deposed = r.Deposed[:n]
2014-09-16 21:34:08 +02:00
}
func (r *ResourceState) sort() {
sort.Strings(r.Dependencies)
}
2014-09-16 21:34:08 +02:00
func (s *ResourceState) GoString() string {
return fmt.Sprintf("*%#v", *s)
2014-09-16 02:06:32 +02:00
}
func (s *ResourceState) String() string {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("Type = %s", s.Type))
return buf.String()
}
2014-09-16 00:31:17 +02:00
// InstanceState is used to track the unique state information belonging
// to a given instance.
type InstanceState struct {
// A unique ID for this resource. This is opaque to Terraform
// and is only meant as a lookup mechanism for the providers.
ID string `json:"id"`
2014-09-16 00:31:17 +02:00
// Attributes are basic information about the resource. Any keys here
// are accessible in variable format within Terraform configurations:
// ${resourcetype.name.attribute}.
Attributes map[string]string `json:"attributes,omitempty"`
2014-09-16 00:31:17 +02:00
// Ephemeral is used to store any state associated with this instance
// that is necessary for the Terraform run to complete, but is not
// persisted to a state file.
Ephemeral EphemeralState `json:"-"`
// Meta is a simple K/V map that is persisted to the State but otherwise
// ignored by Terraform core. It's meant to be used for accounting by
// external client code.
Meta map[string]string `json:"meta,omitempty"`
// Tainted is used to mark a resource for recreation.
Tainted bool `json:"tainted,omitempty"`
}
2014-09-16 21:12:15 +02:00
func (i *InstanceState) init() {
if i.Attributes == nil {
i.Attributes = make(map[string]string)
}
if i.Meta == nil {
i.Meta = make(map[string]string)
}
2014-09-16 21:12:15 +02:00
i.Ephemeral.init()
}
func (i *InstanceState) DeepCopy() *InstanceState {
2014-09-16 21:34:08 +02:00
if i == nil {
return nil
}
n := &InstanceState{
ID: i.ID,
Ephemeral: *i.Ephemeral.DeepCopy(),
Tainted: i.Tainted,
}
if i.Attributes != nil {
n.Attributes = make(map[string]string, len(i.Attributes))
for k, v := range i.Attributes {
n.Attributes[k] = v
}
}
if i.Meta != nil {
n.Meta = make(map[string]string, len(i.Meta))
for k, v := range i.Meta {
n.Meta[k] = v
}
}
return n
}
func (s *InstanceState) Empty() bool {
return s == nil || s.ID == ""
}
2015-02-20 22:39:49 +01:00
func (s *InstanceState) Equal(other *InstanceState) bool {
// Short circuit some nil checks
if s == nil || other == nil {
return s == other
}
// IDs must be equal
if s.ID != other.ID {
return false
}
// Attributes must be equal
if len(s.Attributes) != len(other.Attributes) {
return false
}
for k, v := range s.Attributes {
otherV, ok := other.Attributes[k]
if !ok {
return false
}
if v != otherV {
return false
}
}
// Meta must be equal
if len(s.Meta) != len(other.Meta) {
return false
}
for k, v := range s.Meta {
otherV, ok := other.Meta[k]
if !ok {
return false
}
if v != otherV {
return false
}
}
if s.Tainted != other.Tainted {
return false
}
2015-02-20 22:39:49 +01:00
return true
}
// MergeDiff takes a ResourceDiff and merges the attributes into
// this resource state in order to generate a new state. This new
// state can be used to provide updated attribute lookups for
// variable interpolation.
//
// If the diff attribute requires computing the value, and hence
// won't be available until apply, the value is replaced with the
// computeID.
2014-09-18 01:33:24 +02:00
func (s *InstanceState) MergeDiff(d *InstanceDiff) *InstanceState {
result := s.DeepCopy()
2014-09-18 02:18:03 +02:00
if result == nil {
result = new(InstanceState)
}
result.init()
if s != nil {
for k, v := range s.Attributes {
result.Attributes[k] = v
}
}
if d != nil {
for k, diff := range d.Attributes {
if diff.NewRemoved {
delete(result.Attributes, k)
continue
}
if diff.NewComputed {
result.Attributes[k] = config.UnknownVariableValue
continue
}
result.Attributes[k] = diff.New
}
}
2014-09-18 02:18:03 +02:00
return result
}
2014-09-16 21:34:08 +02:00
func (i *InstanceState) GoString() string {
return fmt.Sprintf("*%#v", *i)
}
2014-09-18 19:40:23 +02:00
func (i *InstanceState) String() string {
var buf bytes.Buffer
if i == nil || i.ID == "" {
2014-09-18 19:40:23 +02:00
return "<not created>"
}
buf.WriteString(fmt.Sprintf("ID = %s\n", i.ID))
attributes := i.Attributes
attrKeys := make([]string, 0, len(attributes))
for ak, _ := range attributes {
if ak == "id" {
continue
}
attrKeys = append(attrKeys, ak)
}
sort.Strings(attrKeys)
for _, ak := range attrKeys {
av := attributes[ak]
buf.WriteString(fmt.Sprintf("%s = %s\n", ak, av))
}
buf.WriteString(fmt.Sprintf("Tainted = %t\n", i.Tainted))
2014-09-18 19:40:23 +02:00
return buf.String()
}
2014-09-16 00:31:17 +02:00
// EphemeralState is used for transient state that is only kept in-memory
type EphemeralState struct {
// ConnInfo is used for the providers to export information which is
// used to connect to the resource for provisioning. For example,
// this could contain SSH or WinRM credentials.
ConnInfo map[string]string `json:"-"`
2016-04-26 20:09:46 +02:00
// Type is used to specify the resource type for this instance. This is only
// required for import operations (as documented). If the documentation
// doesn't state that you need to set this, then don't worry about
// setting it.
Type string `json:"-"`
}
func (e *EphemeralState) init() {
if e.ConnInfo == nil {
e.ConnInfo = make(map[string]string)
}
}
func (e *EphemeralState) DeepCopy() *EphemeralState {
2014-09-16 21:34:08 +02:00
if e == nil {
return nil
}
n := &EphemeralState{}
if e.ConnInfo != nil {
n.ConnInfo = make(map[string]string, len(e.ConnInfo))
for k, v := range e.ConnInfo {
n.ConnInfo[k] = v
}
}
return n
}
type jsonStateVersionIdentifier struct {
Version int `json:"version"`
}
// ReadState reads a state structure out of a reader in the format that
// was written by WriteState.
func ReadState(src io.Reader) (*State, error) {
buf := bufio.NewReader(src)
// Check if this is a V0 format
start, err := buf.Peek(len(stateFormatMagic))
if err != nil {
return nil, fmt.Errorf("Failed to check for magic bytes: %v", err)
}
if string(start) == stateFormatMagic {
// Read the old state
old, err := ReadStateV0(buf)
if err != nil {
return nil, err
}
return old.upgrade()
}
// If we are JSON we buffer the whole thing in memory so we can read it twice.
// This is suboptimal, but will work for now.
jsonBytes, err := ioutil.ReadAll(buf)
if err != nil {
return nil, fmt.Errorf("Reading state file failed: %v", err)
}
versionIdentifier := &jsonStateVersionIdentifier{}
if err := json.Unmarshal(jsonBytes, versionIdentifier); err != nil {
return nil, fmt.Errorf("Decoding state file version failed: %v", err)
}
switch versionIdentifier.Version {
case 0:
return nil, fmt.Errorf("State version 0 is not supported as JSON.")
case 1:
old, err := ReadStateV1(jsonBytes)
if err != nil {
return nil, err
}
return old.upgrade()
case 2:
state, err := ReadStateV2(jsonBytes)
if err != nil {
return nil, err
}
return state, nil
default:
return nil, fmt.Errorf("State version %d not supported, please update.",
versionIdentifier.Version)
}
}
func ReadStateV1(jsonBytes []byte) (*stateV1, error) {
state := &stateV1{}
if err := json.Unmarshal(jsonBytes, state); err != nil {
return nil, fmt.Errorf("Decoding state file failed: %v", err)
}
if state.Version != 1 {
return nil, fmt.Errorf("Decoded state version did not match the decoder selection: "+
"read %d, expected 1", state.Version)
}
return state, nil
}
func ReadStateV2(jsonBytes []byte) (*State, error) {
state := &State{}
if err := json.Unmarshal(jsonBytes, state); err != nil {
return nil, fmt.Errorf("Decoding state file failed: %v", err)
}
// Check the version, this to ensure we don't read a future
// version that we don't understand
2014-10-02 22:45:34 +02:00
if state.Version > StateVersion {
return nil, fmt.Errorf("State version %d not supported, please update.",
state.Version)
}
// Make sure the version is semantic
if state.TFVersion != "" {
if _, err := version.NewVersion(state.TFVersion); err != nil {
return nil, fmt.Errorf(
"State contains invalid version: %s\n\n"+
"Terraform validates the version format prior to writing it. This\n"+
"means that this is invalid of the state becoming corrupted through\n"+
"some external means. Please manually modify the Terraform version\n"+
"field to be a proper semantic version.",
state.TFVersion)
}
}
// Sort it
state.sort()
return state, nil
}
// WriteState writes a state somewhere in a binary format.
func WriteState(d *State, dst io.Writer) error {
// Make sure it is sorted
d.sort()
// Ensure the version is set
2014-10-02 22:45:34 +02:00
d.Version = StateVersion
// If the TFVersion is set, verify it. We used to just set the version
// here, but this isn't safe since it changes the MD5 sum on some remote
// state storage backends such as Atlas. We now leave it be if needed.
if d.TFVersion != "" {
if _, err := version.NewVersion(d.TFVersion); err != nil {
return fmt.Errorf(
"Error writing state, invalid version: %s\n\n"+
"The Terraform version when writing the state must be a semantic\n"+
"version.",
d.TFVersion)
}
}
// Encode the data in a human-friendly way
data, err := json.MarshalIndent(d, "", " ")
if err != nil {
return fmt.Errorf("Failed to encode state: %s", err)
}
// We append a newline to the data because MarshalIndent doesn't
data = append(data, '\n')
// Write the data out to the dst
if _, err := io.Copy(dst, bytes.NewReader(data)); err != nil {
return fmt.Errorf("Failed to write state: %v", err)
}
2014-09-16 21:34:08 +02:00
return nil
}
// moduleStateSort implements sort.Interface to sort module states
type moduleStateSort []*ModuleState
func (s moduleStateSort) Len() int {
return len(s)
}
func (s moduleStateSort) Less(i, j int) bool {
a := s[i]
b := s[j]
// If the lengths are different, then the shorter one always wins
if len(a.Path) != len(b.Path) {
return len(a.Path) < len(b.Path)
}
// Otherwise, compare lexically
return strings.Join(a.Path, ".") < strings.Join(b.Path, ".")
}
func (s moduleStateSort) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}