github.com/adamar/terraform@v0.2.2-0.20141016210445-2e703afdad0e/terraform/state.go (about)

     1  package terraform
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"log"
    10  	"reflect"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/hashicorp/terraform/config"
    15  )
    16  
    17  const (
    18  	// textStateVersion is the current version for our state file
    19  	textStateVersion = 1
    20  )
    21  
    22  // rootModulePath is the path of the root module
    23  var rootModulePath = []string{"root"}
    24  
    25  // State keeps track of a snapshot state-of-the-world that Terraform
    26  // can use to keep track of what real world resources it is actually
    27  // managing. This is the latest format as of Terraform 0.3
    28  type State struct {
    29  	// Version is the protocol version. Currently only "1".
    30  	Version int `json:"version"`
    31  
    32  	// Serial is incremented on any operation that modifies
    33  	// the State file. It is used to detect potentially conflicting
    34  	// updates.
    35  	Serial int64 `json:"serial"`
    36  
    37  	// Modules contains all the modules in a breadth-first order
    38  	Modules []*ModuleState `json:"modules"`
    39  }
    40  
    41  // Children returns the ModuleStates that are direct children of
    42  // the given path. If the path is "root", for example, then children
    43  // returned might be "root.child", but not "root.child.grandchild".
    44  func (s *State) Children(path []string) []*ModuleState {
    45  	// TODO: test
    46  
    47  	result := make([]*ModuleState, 0)
    48  	for _, m := range s.Modules {
    49  		if len(m.Path) != len(path)+1 {
    50  			continue
    51  		}
    52  		if !reflect.DeepEqual(path, m.Path[:len(path)]) {
    53  			continue
    54  		}
    55  
    56  		result = append(result, m)
    57  	}
    58  
    59  	return result
    60  }
    61  
    62  // AddModule adds the module with the given path to the state.
    63  //
    64  // This should be the preferred method to add module states since it
    65  // allows us to optimize lookups later as well as control sorting.
    66  func (s *State) AddModule(path []string) *ModuleState {
    67  	m := &ModuleState{Path: path}
    68  	m.init()
    69  	s.Modules = append(s.Modules, m)
    70  	s.sort()
    71  	return m
    72  }
    73  
    74  // ModuleByPath is used to lookup the module state for the given path.
    75  // This should be the prefered lookup mechanism as it allows for future
    76  // lookup optimizations.
    77  func (s *State) ModuleByPath(path []string) *ModuleState {
    78  	if s == nil {
    79  		return nil
    80  	}
    81  	for _, mod := range s.Modules {
    82  		if mod.Path == nil {
    83  			panic("missing module path")
    84  		}
    85  		if reflect.DeepEqual(mod.Path, path) {
    86  			return mod
    87  		}
    88  	}
    89  	return nil
    90  }
    91  
    92  // RootModule returns the ModuleState for the root module
    93  func (s *State) RootModule() *ModuleState {
    94  	root := s.ModuleByPath(rootModulePath)
    95  	if root == nil {
    96  		panic("missing root module")
    97  	}
    98  	return root
    99  }
   100  
   101  func (s *State) init() {
   102  	if s.Version == 0 {
   103  		s.Version = textStateVersion
   104  	}
   105  	if len(s.Modules) == 0 {
   106  		root := &ModuleState{
   107  			Path: rootModulePath,
   108  		}
   109  		root.init()
   110  		s.Modules = []*ModuleState{root}
   111  	}
   112  }
   113  
   114  func (s *State) deepcopy() *State {
   115  	if s == nil {
   116  		return nil
   117  	}
   118  	n := &State{
   119  		Version: s.Version,
   120  		Serial:  s.Serial,
   121  		Modules: make([]*ModuleState, 0, len(s.Modules)),
   122  	}
   123  	for _, mod := range s.Modules {
   124  		n.Modules = append(n.Modules, mod.deepcopy())
   125  	}
   126  	return n
   127  }
   128  
   129  // prune is used to remove any resources that are no longer required
   130  func (s *State) prune() {
   131  	if s == nil {
   132  		return
   133  	}
   134  	for _, mod := range s.Modules {
   135  		mod.prune()
   136  	}
   137  }
   138  
   139  // sort sorts the modules
   140  func (s *State) sort() {
   141  	sort.Sort(moduleStateSort(s.Modules))
   142  }
   143  
   144  func (s *State) GoString() string {
   145  	return fmt.Sprintf("*%#v", *s)
   146  }
   147  
   148  func (s *State) String() string {
   149  	var buf bytes.Buffer
   150  	for _, m := range s.Modules {
   151  		mStr := m.String()
   152  
   153  		// If we're the root module, we just write the output directly.
   154  		if reflect.DeepEqual(m.Path, rootModulePath) {
   155  			buf.WriteString(mStr + "\n")
   156  			continue
   157  		}
   158  
   159  		buf.WriteString(fmt.Sprintf("module.%s:\n", strings.Join(m.Path[1:], ".")))
   160  
   161  		s := bufio.NewScanner(strings.NewReader(mStr))
   162  		for s.Scan() {
   163  			buf.WriteString(fmt.Sprintf("  %s\n", s.Text()))
   164  		}
   165  	}
   166  
   167  	return strings.TrimSpace(buf.String())
   168  }
   169  
   170  // ModuleState is used to track all the state relevant to a single
   171  // module. Previous to Terraform 0.3, all state belonged to the "root"
   172  // module.
   173  type ModuleState struct {
   174  	// Path is the import path from the root module. Modules imports are
   175  	// always disjoint, so the path represents amodule tree
   176  	Path []string `json:"path"`
   177  
   178  	// Outputs declared by the module and maintained for each module
   179  	// even though only the root module technically needs to be kept.
   180  	// This allows operators to inspect values at the boundaries.
   181  	Outputs map[string]string `json:"outputs"`
   182  
   183  	// Resources is a mapping of the logically named resource to
   184  	// the state of the resource. Each resource may actually have
   185  	// N instances underneath, although a user only needs to think
   186  	// about the 1:1 case.
   187  	Resources map[string]*ResourceState `json:"resources"`
   188  }
   189  
   190  // IsRoot says whether or not this module diff is for the root module.
   191  func (m *ModuleState) IsRoot() bool {
   192  	return reflect.DeepEqual(m.Path, rootModulePath)
   193  }
   194  
   195  // Orphans returns a list of keys of resources that are in the State
   196  // but aren't present in the configuration itself. Hence, these keys
   197  // represent the state of resources that are orphans.
   198  func (m *ModuleState) Orphans(c *config.Config) []string {
   199  	keys := make(map[string]struct{})
   200  	for k, _ := range m.Resources {
   201  		keys[k] = struct{}{}
   202  	}
   203  
   204  	for _, r := range c.Resources {
   205  		delete(keys, r.Id())
   206  
   207  		for k, _ := range keys {
   208  			if strings.HasPrefix(k, r.Id()+".") {
   209  				delete(keys, k)
   210  			}
   211  		}
   212  	}
   213  
   214  	result := make([]string, 0, len(keys))
   215  	for k, _ := range keys {
   216  		result = append(result, k)
   217  	}
   218  
   219  	return result
   220  }
   221  
   222  // View returns a view with the given resource prefix.
   223  func (m *ModuleState) View(id string) *ModuleState {
   224  	if m == nil {
   225  		return m
   226  	}
   227  
   228  	r := m.deepcopy()
   229  	for k, _ := range r.Resources {
   230  		if id == k || strings.HasPrefix(k, id+".") {
   231  			continue
   232  		}
   233  
   234  		delete(r.Resources, k)
   235  	}
   236  
   237  	return r
   238  }
   239  
   240  func (m *ModuleState) init() {
   241  	if m.Outputs == nil {
   242  		m.Outputs = make(map[string]string)
   243  	}
   244  	if m.Resources == nil {
   245  		m.Resources = make(map[string]*ResourceState)
   246  	}
   247  }
   248  
   249  func (m *ModuleState) deepcopy() *ModuleState {
   250  	if m == nil {
   251  		return nil
   252  	}
   253  	n := &ModuleState{
   254  		Path:      make([]string, len(m.Path)),
   255  		Outputs:   make(map[string]string, len(m.Outputs)),
   256  		Resources: make(map[string]*ResourceState, len(m.Resources)),
   257  	}
   258  	copy(n.Path, m.Path)
   259  	for k, v := range m.Outputs {
   260  		n.Outputs[k] = v
   261  	}
   262  	for k, v := range m.Resources {
   263  		n.Resources[k] = v.deepcopy()
   264  	}
   265  	return n
   266  }
   267  
   268  // prune is used to remove any resources that are no longer required
   269  func (m *ModuleState) prune() {
   270  	for k, v := range m.Resources {
   271  		v.prune()
   272  		if (v.Primary == nil || v.Primary.ID == "") && len(v.Tainted) == 0 {
   273  			delete(m.Resources, k)
   274  		}
   275  	}
   276  }
   277  
   278  func (m *ModuleState) GoString() string {
   279  	return fmt.Sprintf("*%#v", *m)
   280  }
   281  
   282  func (m *ModuleState) String() string {
   283  	if len(m.Resources) == 0 {
   284  		return "<no state>"
   285  	}
   286  
   287  	var buf bytes.Buffer
   288  
   289  	names := make([]string, 0, len(m.Resources))
   290  	for name, _ := range m.Resources {
   291  		names = append(names, name)
   292  	}
   293  	sort.Strings(names)
   294  
   295  	for _, k := range names {
   296  		rs := m.Resources[k]
   297  		var id string
   298  		if rs.Primary != nil {
   299  			id = rs.Primary.ID
   300  		}
   301  		if id == "" {
   302  			id = "<not created>"
   303  		}
   304  
   305  		taintStr := ""
   306  		if len(rs.Tainted) > 0 {
   307  			taintStr = fmt.Sprintf(" (%d tainted)", len(rs.Tainted))
   308  		}
   309  
   310  		buf.WriteString(fmt.Sprintf("%s:%s\n", k, taintStr))
   311  		buf.WriteString(fmt.Sprintf("  ID = %s\n", id))
   312  
   313  		var attributes map[string]string
   314  		if rs.Primary != nil {
   315  			attributes = rs.Primary.Attributes
   316  		}
   317  		attrKeys := make([]string, 0, len(attributes))
   318  		for ak, _ := range attributes {
   319  			if ak == "id" {
   320  				continue
   321  			}
   322  
   323  			attrKeys = append(attrKeys, ak)
   324  		}
   325  		sort.Strings(attrKeys)
   326  
   327  		for _, ak := range attrKeys {
   328  			av := attributes[ak]
   329  			buf.WriteString(fmt.Sprintf("  %s = %s\n", ak, av))
   330  		}
   331  
   332  		for idx, t := range rs.Tainted {
   333  			buf.WriteString(fmt.Sprintf("  Tainted ID %d = %s\n", idx+1, t.ID))
   334  		}
   335  
   336  		if len(rs.Dependencies) > 0 {
   337  			buf.WriteString(fmt.Sprintf("\n  Dependencies:\n"))
   338  			for _, dep := range rs.Dependencies {
   339  				buf.WriteString(fmt.Sprintf("    %s\n", dep))
   340  			}
   341  		}
   342  	}
   343  
   344  	if len(m.Outputs) > 0 {
   345  		buf.WriteString("\nOutputs:\n\n")
   346  
   347  		ks := make([]string, 0, len(m.Outputs))
   348  		for k, _ := range m.Outputs {
   349  			ks = append(ks, k)
   350  		}
   351  		sort.Strings(ks)
   352  
   353  		for _, k := range ks {
   354  			v := m.Outputs[k]
   355  			buf.WriteString(fmt.Sprintf("%s = %s\n", k, v))
   356  		}
   357  	}
   358  
   359  	return buf.String()
   360  }
   361  
   362  // ResourceState holds the state of a resource that is used so that
   363  // a provider can find and manage an existing resource as well as for
   364  // storing attributes that are used to populate variables of child
   365  // resources.
   366  //
   367  // Attributes has attributes about the created resource that are
   368  // queryable in interpolation: "${type.id.attr}"
   369  //
   370  // Extra is just extra data that a provider can return that we store
   371  // for later, but is not exposed in any way to the user.
   372  //
   373  type ResourceState struct {
   374  	// This is filled in and managed by Terraform, and is the resource
   375  	// type itself such as "mycloud_instance". If a resource provider sets
   376  	// this value, it won't be persisted.
   377  	Type string `json:"type"`
   378  
   379  	// Dependencies are a list of things that this resource relies on
   380  	// existing to remain intact. For example: an AWS instance might
   381  	// depend on a subnet (which itself might depend on a VPC, and so
   382  	// on).
   383  	//
   384  	// Terraform uses this information to build valid destruction
   385  	// orders and to warn the user if they're destroying a resource that
   386  	// another resource depends on.
   387  	//
   388  	// Things can be put into this list that may not be managed by
   389  	// Terraform. If Terraform doesn't find a matching ID in the
   390  	// overall state, then it assumes it isn't managed and doesn't
   391  	// worry about it.
   392  	Dependencies []string `json:"depends_on,omitempty"`
   393  
   394  	// Primary is the current active instance for this resource.
   395  	// It can be replaced but only after a successful creation.
   396  	// This is the instances on which providers will act.
   397  	Primary *InstanceState `json:"primary"`
   398  
   399  	// Tainted is used to track any underlying instances that
   400  	// have been created but are in a bad or unknown state and
   401  	// need to be cleaned up subsequently.  In the
   402  	// standard case, there is only at most a single instance.
   403  	// However, in pathological cases, it is possible for the number
   404  	// of instances to accumulate.
   405  	Tainted []*InstanceState `json:"tainted,omitempty"`
   406  }
   407  
   408  func (r *ResourceState) init() {
   409  	if r.Primary == nil {
   410  		r.Primary = &InstanceState{}
   411  	}
   412  	r.Primary.init()
   413  }
   414  
   415  func (r *ResourceState) deepcopy() *ResourceState {
   416  	if r == nil {
   417  		return nil
   418  	}
   419  	n := &ResourceState{
   420  		Type:         r.Type,
   421  		Dependencies: make([]string, len(r.Dependencies)),
   422  		Primary:      r.Primary.deepcopy(),
   423  		Tainted:      make([]*InstanceState, 0, len(r.Tainted)),
   424  	}
   425  	copy(n.Dependencies, r.Dependencies)
   426  	for _, inst := range r.Tainted {
   427  		n.Tainted = append(n.Tainted, inst.deepcopy())
   428  	}
   429  	return n
   430  }
   431  
   432  // prune is used to remove any instances that are no longer required
   433  func (r *ResourceState) prune() {
   434  	n := len(r.Tainted)
   435  	for i := 0; i < n; i++ {
   436  		inst := r.Tainted[i]
   437  		if inst.ID == "" {
   438  			copy(r.Tainted[i:], r.Tainted[i+1:])
   439  			r.Tainted[n-1] = nil
   440  			n--
   441  		}
   442  	}
   443  	r.Tainted = r.Tainted[:n]
   444  }
   445  
   446  func (s *ResourceState) GoString() string {
   447  	return fmt.Sprintf("*%#v", *s)
   448  }
   449  
   450  func (s *ResourceState) String() string {
   451  	var buf bytes.Buffer
   452  	buf.WriteString(fmt.Sprintf("Type = %s", s.Type))
   453  	return buf.String()
   454  }
   455  
   456  // InstanceState is used to track the unique state information belonging
   457  // to a given instance.
   458  type InstanceState struct {
   459  	// A unique ID for this resource. This is opaque to Terraform
   460  	// and is only meant as a lookup mechanism for the providers.
   461  	ID string `json:"id"`
   462  
   463  	// Attributes are basic information about the resource. Any keys here
   464  	// are accessible in variable format within Terraform configurations:
   465  	// ${resourcetype.name.attribute}.
   466  	Attributes map[string]string `json:"attributes,omitempty"`
   467  
   468  	// Ephemeral is used to store any state associated with this instance
   469  	// that is necessary for the Terraform run to complete, but is not
   470  	// persisted to a state file.
   471  	Ephemeral EphemeralState `json:"-"`
   472  }
   473  
   474  func (i *InstanceState) init() {
   475  	if i.Attributes == nil {
   476  		i.Attributes = make(map[string]string)
   477  	}
   478  	i.Ephemeral.init()
   479  }
   480  
   481  func (i *InstanceState) deepcopy() *InstanceState {
   482  	if i == nil {
   483  		return nil
   484  	}
   485  	n := &InstanceState{
   486  		ID:        i.ID,
   487  		Ephemeral: *i.Ephemeral.deepcopy(),
   488  	}
   489  	if i.Attributes != nil {
   490  		n.Attributes = make(map[string]string, len(i.Attributes))
   491  		for k, v := range i.Attributes {
   492  			n.Attributes[k] = v
   493  		}
   494  	}
   495  	return n
   496  }
   497  
   498  // MergeDiff takes a ResourceDiff and merges the attributes into
   499  // this resource state in order to generate a new state. This new
   500  // state can be used to provide updated attribute lookups for
   501  // variable interpolation.
   502  //
   503  // If the diff attribute requires computing the value, and hence
   504  // won't be available until apply, the value is replaced with the
   505  // computeID.
   506  func (s *InstanceState) MergeDiff(d *InstanceDiff) *InstanceState {
   507  	result := s.deepcopy()
   508  	if result == nil {
   509  		result = new(InstanceState)
   510  	}
   511  	result.init()
   512  
   513  	if s != nil {
   514  		for k, v := range s.Attributes {
   515  			result.Attributes[k] = v
   516  		}
   517  	}
   518  	if d != nil {
   519  		for k, diff := range d.Attributes {
   520  			if diff.NewRemoved {
   521  				delete(result.Attributes, k)
   522  				continue
   523  			}
   524  			if diff.NewComputed {
   525  				result.Attributes[k] = config.UnknownVariableValue
   526  				continue
   527  			}
   528  
   529  			result.Attributes[k] = diff.New
   530  		}
   531  	}
   532  
   533  	return result
   534  }
   535  
   536  func (i *InstanceState) GoString() string {
   537  	return fmt.Sprintf("*%#v", *i)
   538  }
   539  
   540  func (i *InstanceState) String() string {
   541  	var buf bytes.Buffer
   542  
   543  	if i == nil || i.ID == "" {
   544  		return "<not created>"
   545  	}
   546  
   547  	buf.WriteString(fmt.Sprintf("ID = %s\n", i.ID))
   548  
   549  	attributes := i.Attributes
   550  	attrKeys := make([]string, 0, len(attributes))
   551  	for ak, _ := range attributes {
   552  		if ak == "id" {
   553  			continue
   554  		}
   555  
   556  		attrKeys = append(attrKeys, ak)
   557  	}
   558  	sort.Strings(attrKeys)
   559  
   560  	for _, ak := range attrKeys {
   561  		av := attributes[ak]
   562  		buf.WriteString(fmt.Sprintf("%s = %s\n", ak, av))
   563  	}
   564  
   565  	return buf.String()
   566  }
   567  
   568  // EphemeralState is used for transient state that is only kept in-memory
   569  type EphemeralState struct {
   570  	// ConnInfo is used for the providers to export information which is
   571  	// used to connect to the resource for provisioning. For example,
   572  	// this could contain SSH or WinRM credentials.
   573  	ConnInfo map[string]string `json:"-"`
   574  }
   575  
   576  func (e *EphemeralState) init() {
   577  	if e.ConnInfo == nil {
   578  		e.ConnInfo = make(map[string]string)
   579  	}
   580  }
   581  
   582  func (e *EphemeralState) deepcopy() *EphemeralState {
   583  	if e == nil {
   584  		return nil
   585  	}
   586  	n := &EphemeralState{}
   587  	if e.ConnInfo != nil {
   588  		n.ConnInfo = make(map[string]string, len(e.ConnInfo))
   589  		for k, v := range e.ConnInfo {
   590  			n.ConnInfo[k] = v
   591  		}
   592  	}
   593  	return n
   594  }
   595  
   596  // ReadState reads a state structure out of a reader in the format that
   597  // was written by WriteState.
   598  func ReadState(src io.Reader) (*State, error) {
   599  	buf := bufio.NewReader(src)
   600  
   601  	// Check if this is a V1 format
   602  	start, err := buf.Peek(len(stateFormatMagic))
   603  	if err != nil {
   604  		return nil, fmt.Errorf("Failed to check for magic bytes: %v", err)
   605  	}
   606  	if string(start) == stateFormatMagic {
   607  		// Read the old state
   608  		old, err := ReadStateV1(buf)
   609  		if err != nil {
   610  			return nil, err
   611  		}
   612  		return upgradeV1State(old)
   613  	}
   614  
   615  	// Otherwise, must be V2
   616  	dec := json.NewDecoder(buf)
   617  	state := &State{}
   618  	if err := dec.Decode(state); err != nil {
   619  		return nil, fmt.Errorf("Decoding state file failed: %v", err)
   620  	}
   621  
   622  	// Check the version, this to ensure we don't read a future
   623  	// version that we don't understand
   624  	if state.Version > textStateVersion {
   625  		return nil, fmt.Errorf("State version %d not supported, please update.",
   626  			state.Version)
   627  	}
   628  
   629  	// Sort it
   630  	state.sort()
   631  
   632  	return state, nil
   633  }
   634  
   635  // WriteState writes a state somewhere in a binary format.
   636  func WriteState(d *State, dst io.Writer) error {
   637  	// Make sure it is sorted
   638  	d.sort()
   639  
   640  	// Ensure the version is set
   641  	d.Version = textStateVersion
   642  
   643  	// Always increment the serial number
   644  	d.Serial++
   645  
   646  	// Encode the data in a human-friendly way
   647  	data, err := json.MarshalIndent(d, "", "    ")
   648  	if err != nil {
   649  		return fmt.Errorf("Failed to encode state: %s", err)
   650  	}
   651  
   652  	// We append a newline to the data because MarshalIndent doesn't
   653  	data = append(data, '\n')
   654  
   655  	// Write the data out to the dst
   656  	if _, err := io.Copy(dst, bytes.NewReader(data)); err != nil {
   657  		return fmt.Errorf("Failed to write state: %v", err)
   658  	}
   659  
   660  	return nil
   661  }
   662  
   663  // upgradeV1State is used to upgrade a V1 state representation
   664  // into a proper State representation.
   665  func upgradeV1State(old *StateV1) (*State, error) {
   666  	s := &State{}
   667  	s.init()
   668  
   669  	// Old format had no modules, so we migrate everything
   670  	// directly into the root module.
   671  	root := s.RootModule()
   672  
   673  	// Copy the outputs
   674  	root.Outputs = old.Outputs
   675  
   676  	// Upgrade the resources
   677  	for id, rs := range old.Resources {
   678  		newRs := &ResourceState{
   679  			Type: rs.Type,
   680  		}
   681  		root.Resources[id] = newRs
   682  
   683  		// Migrate to an instance state
   684  		instance := &InstanceState{
   685  			ID:         rs.ID,
   686  			Attributes: rs.Attributes,
   687  		}
   688  
   689  		// Check if this is the primary or tainted instance
   690  		if _, ok := old.Tainted[id]; ok {
   691  			newRs.Tainted = append(newRs.Tainted, instance)
   692  		} else {
   693  			newRs.Primary = instance
   694  		}
   695  
   696  		// Warn if the resource uses Extra, as there is
   697  		// no upgrade path for this! Now totally deprecated.
   698  		if len(rs.Extra) > 0 {
   699  			log.Printf(
   700  				"[WARN] Resource %s uses deprecated attribute "+
   701  					"storage, state file upgrade may be incomplete.",
   702  				rs.ID,
   703  			)
   704  		}
   705  	}
   706  	return s, nil
   707  }
   708  
   709  // moduleStateSort implements sort.Interface to sort module states
   710  type moduleStateSort []*ModuleState
   711  
   712  func (s moduleStateSort) Len() int {
   713  	return len(s)
   714  }
   715  
   716  func (s moduleStateSort) Less(i, j int) bool {
   717  	a := s[i]
   718  	b := s[j]
   719  
   720  	// If the lengths are different, then the shorter one always wins
   721  	if len(a.Path) != len(b.Path) {
   722  		return len(a.Path) < len(b.Path)
   723  	}
   724  
   725  	// Otherwise, compare by last path element
   726  	idx := len(a.Path) - 1
   727  	return a.Path[idx] < b.Path[idx]
   728  }
   729  
   730  func (s moduleStateSort) Swap(i, j int) {
   731  	s[i], s[j] = s[j], s[i]
   732  }