github.com/ricardclau/terraform@v0.6.17-0.20160519222547-283e3ae6b5a9/terraform/state_v0.go (about)

     1  package terraform
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/gob"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"sort"
    10  	"strings"
    11  	"sync"
    12  
    13  	"github.com/hashicorp/terraform/config"
    14  	"log"
    15  )
    16  
    17  // The format byte is prefixed into the state file format so that we have
    18  // the ability in the future to change the file format if we want for any
    19  // reason.
    20  const (
    21  	stateFormatMagic        = "tfstate"
    22  	stateFormatVersion byte = 1
    23  )
    24  
    25  // StateV0 is used to represent the state of Terraform files before
    26  // 0.3. It is automatically upgraded to a modern State representation
    27  // on start.
    28  type StateV0 struct {
    29  	Outputs   map[string]string
    30  	Resources map[string]*ResourceStateV0
    31  	Tainted   map[string]struct{}
    32  
    33  	once sync.Once
    34  }
    35  
    36  func (s *StateV0) init() {
    37  	s.once.Do(func() {
    38  		if s.Resources == nil {
    39  			s.Resources = make(map[string]*ResourceStateV0)
    40  		}
    41  
    42  		if s.Tainted == nil {
    43  			s.Tainted = make(map[string]struct{})
    44  		}
    45  	})
    46  }
    47  
    48  func (s *StateV0) deepcopy() *StateV0 {
    49  	result := new(StateV0)
    50  	result.init()
    51  	if s != nil {
    52  		for k, v := range s.Resources {
    53  			result.Resources[k] = v
    54  		}
    55  		for k, v := range s.Tainted {
    56  			result.Tainted[k] = v
    57  		}
    58  	}
    59  
    60  	return result
    61  }
    62  
    63  // prune is a helper that removes any empty IDs from the state
    64  // and cleans it up in general.
    65  func (s *StateV0) prune() {
    66  	for k, v := range s.Resources {
    67  		if v.ID == "" {
    68  			delete(s.Resources, k)
    69  		}
    70  	}
    71  }
    72  
    73  // Orphans returns a list of keys of resources that are in the State
    74  // but aren't present in the configuration itself. Hence, these keys
    75  // represent the state of resources that are orphans.
    76  func (s *StateV0) Orphans(c *config.Config) []string {
    77  	keys := make(map[string]struct{})
    78  	for k, _ := range s.Resources {
    79  		keys[k] = struct{}{}
    80  	}
    81  
    82  	for _, r := range c.Resources {
    83  		delete(keys, r.Id())
    84  
    85  		for k, _ := range keys {
    86  			if strings.HasPrefix(k, r.Id()+".") {
    87  				delete(keys, k)
    88  			}
    89  		}
    90  	}
    91  
    92  	result := make([]string, 0, len(keys))
    93  	for k, _ := range keys {
    94  		result = append(result, k)
    95  	}
    96  
    97  	return result
    98  }
    99  
   100  func (s *StateV0) String() string {
   101  	if len(s.Resources) == 0 {
   102  		return "<no state>"
   103  	}
   104  
   105  	var buf bytes.Buffer
   106  
   107  	names := make([]string, 0, len(s.Resources))
   108  	for name, _ := range s.Resources {
   109  		names = append(names, name)
   110  	}
   111  	sort.Strings(names)
   112  
   113  	for _, k := range names {
   114  		rs := s.Resources[k]
   115  		id := rs.ID
   116  		if id == "" {
   117  			id = "<not created>"
   118  		}
   119  
   120  		taintStr := ""
   121  		if _, ok := s.Tainted[k]; ok {
   122  			taintStr = " (tainted)"
   123  		}
   124  
   125  		buf.WriteString(fmt.Sprintf("%s:%s\n", k, taintStr))
   126  		buf.WriteString(fmt.Sprintf("  ID = %s\n", id))
   127  
   128  		attrKeys := make([]string, 0, len(rs.Attributes))
   129  		for ak, _ := range rs.Attributes {
   130  			if ak == "id" {
   131  				continue
   132  			}
   133  
   134  			attrKeys = append(attrKeys, ak)
   135  		}
   136  		sort.Strings(attrKeys)
   137  
   138  		for _, ak := range attrKeys {
   139  			av := rs.Attributes[ak]
   140  			buf.WriteString(fmt.Sprintf("  %s = %s\n", ak, av))
   141  		}
   142  
   143  		if len(rs.Dependencies) > 0 {
   144  			buf.WriteString(fmt.Sprintf("\n  Dependencies:\n"))
   145  			for _, dep := range rs.Dependencies {
   146  				buf.WriteString(fmt.Sprintf("    %s\n", dep.ID))
   147  			}
   148  		}
   149  	}
   150  
   151  	if len(s.Outputs) > 0 {
   152  		buf.WriteString("\nOutputs:\n\n")
   153  
   154  		ks := make([]string, 0, len(s.Outputs))
   155  		for k, _ := range s.Outputs {
   156  			ks = append(ks, k)
   157  		}
   158  		sort.Strings(ks)
   159  
   160  		for _, k := range ks {
   161  			v := s.Outputs[k]
   162  			buf.WriteString(fmt.Sprintf("%s = %s\n", k, v))
   163  		}
   164  	}
   165  
   166  	return buf.String()
   167  }
   168  
   169  /// ResourceState holds the state of a resource that is used so that
   170  // a provider can find and manage an existing resource as well as for
   171  // storing attributes that are uesd to populate variables of child
   172  // resources.
   173  //
   174  // Attributes has attributes about the created resource that are
   175  // queryable in interpolation: "${type.id.attr}"
   176  //
   177  // Extra is just extra data that a provider can return that we store
   178  // for later, but is not exposed in any way to the user.
   179  type ResourceStateV0 struct {
   180  	// This is filled in and managed by Terraform, and is the resource
   181  	// type itself such as "mycloud_instance". If a resource provider sets
   182  	// this value, it won't be persisted.
   183  	Type string
   184  
   185  	// The attributes below are all meant to be filled in by the
   186  	// resource providers themselves. Documentation for each are above
   187  	// each element.
   188  
   189  	// A unique ID for this resource. This is opaque to Terraform
   190  	// and is only meant as a lookup mechanism for the providers.
   191  	ID string
   192  
   193  	// Attributes are basic information about the resource. Any keys here
   194  	// are accessible in variable format within Terraform configurations:
   195  	// ${resourcetype.name.attribute}.
   196  	Attributes map[string]string
   197  
   198  	// ConnInfo is used for the providers to export information which is
   199  	// used to connect to the resource for provisioning. For example,
   200  	// this could contain SSH or WinRM credentials.
   201  	ConnInfo map[string]string
   202  
   203  	// Extra information that the provider can store about a resource.
   204  	// This data is opaque, never shown to the user, and is sent back to
   205  	// the provider as-is for whatever purpose appropriate.
   206  	Extra map[string]interface{}
   207  
   208  	// Dependencies are a list of things that this resource relies on
   209  	// existing to remain intact. For example: an AWS instance might
   210  	// depend on a subnet (which itself might depend on a VPC, and so
   211  	// on).
   212  	//
   213  	// Terraform uses this information to build valid destruction
   214  	// orders and to warn the user if they're destroying a resource that
   215  	// another resource depends on.
   216  	//
   217  	// Things can be put into this list that may not be managed by
   218  	// Terraform. If Terraform doesn't find a matching ID in the
   219  	// overall state, then it assumes it isn't managed and doesn't
   220  	// worry about it.
   221  	Dependencies []ResourceDependency
   222  }
   223  
   224  // MergeDiff takes a ResourceDiff and merges the attributes into
   225  // this resource state in order to generate a new state. This new
   226  // state can be used to provide updated attribute lookups for
   227  // variable interpolation.
   228  //
   229  // If the diff attribute requires computing the value, and hence
   230  // won't be available until apply, the value is replaced with the
   231  // computeID.
   232  func (s *ResourceStateV0) MergeDiff(d *InstanceDiff) *ResourceStateV0 {
   233  	var result ResourceStateV0
   234  	if s != nil {
   235  		result = *s
   236  	}
   237  
   238  	result.Attributes = make(map[string]string)
   239  	if s != nil {
   240  		for k, v := range s.Attributes {
   241  			result.Attributes[k] = v
   242  		}
   243  	}
   244  	if d != nil {
   245  		for k, diff := range d.Attributes {
   246  			if diff.NewRemoved {
   247  				delete(result.Attributes, k)
   248  				continue
   249  			}
   250  			if diff.NewComputed {
   251  				result.Attributes[k] = config.UnknownVariableValue
   252  				continue
   253  			}
   254  
   255  			result.Attributes[k] = diff.New
   256  		}
   257  	}
   258  
   259  	return &result
   260  }
   261  
   262  func (s *ResourceStateV0) GoString() string {
   263  	return fmt.Sprintf("*%#v", *s)
   264  }
   265  
   266  // ResourceDependency maps a resource to another resource that it
   267  // depends on to remain intact and uncorrupted.
   268  type ResourceDependency struct {
   269  	// ID of the resource that we depend on. This ID should map
   270  	// directly to another ResourceState's ID.
   271  	ID string
   272  }
   273  
   274  // ReadStateV0 reads a state structure out of a reader in the format that
   275  // was written by WriteState.
   276  func ReadStateV0(src io.Reader) (*StateV0, error) {
   277  	var result *StateV0
   278  	var err error
   279  	n := 0
   280  
   281  	// Verify the magic bytes
   282  	magic := make([]byte, len(stateFormatMagic))
   283  	for n < len(magic) {
   284  		n, err = src.Read(magic[n:])
   285  		if err != nil {
   286  			return nil, fmt.Errorf("error while reading magic bytes: %s", err)
   287  		}
   288  	}
   289  	if string(magic) != stateFormatMagic {
   290  		return nil, fmt.Errorf("not a valid state file")
   291  	}
   292  
   293  	// Verify the version is something we can read
   294  	var formatByte [1]byte
   295  	n, err = src.Read(formatByte[:])
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  	if n != len(formatByte) {
   300  		return nil, errors.New("failed to read state version byte")
   301  	}
   302  
   303  	if formatByte[0] != stateFormatVersion {
   304  		return nil, fmt.Errorf("unknown state file version: %d", formatByte[0])
   305  	}
   306  
   307  	// Decode
   308  	dec := gob.NewDecoder(src)
   309  	if err := dec.Decode(&result); err != nil {
   310  		return nil, err
   311  	}
   312  
   313  	return result, nil
   314  }
   315  
   316  // upgradeV0State is used to upgrade a V0 state representation
   317  // into a State (current) representation.
   318  func (old *StateV0) upgrade() (*State, error) {
   319  	s := &State{}
   320  	s.init()
   321  
   322  	// Old format had no modules, so we migrate everything
   323  	// directly into the root module.
   324  	root := s.RootModule()
   325  
   326  	// Copy the outputs, first converting them to map[string]interface{}
   327  	oldOutputs := make(map[string]*OutputState, len(old.Outputs))
   328  	for key, value := range old.Outputs {
   329  		oldOutputs[key] = &OutputState{
   330  			Type:      "string",
   331  			Sensitive: false,
   332  			Value:     value,
   333  		}
   334  	}
   335  	root.Outputs = oldOutputs
   336  
   337  	// Upgrade the resources
   338  	for id, rs := range old.Resources {
   339  		newRs := &ResourceState{
   340  			Type: rs.Type,
   341  		}
   342  		root.Resources[id] = newRs
   343  
   344  		// Migrate to an instance state
   345  		instance := &InstanceState{
   346  			ID:         rs.ID,
   347  			Attributes: rs.Attributes,
   348  		}
   349  
   350  		// Check if this is the primary or tainted instance
   351  		if _, ok := old.Tainted[id]; ok {
   352  			newRs.Tainted = append(newRs.Tainted, instance)
   353  		} else {
   354  			newRs.Primary = instance
   355  		}
   356  
   357  		// Warn if the resource uses Extra, as there is
   358  		// no upgrade path for this! Now totally deprecated.
   359  		if len(rs.Extra) > 0 {
   360  			log.Printf(
   361  				"[WARN] Resource %s uses deprecated attribute "+
   362  					"storage, state file upgrade may be incomplete.",
   363  				rs.ID,
   364  			)
   365  		}
   366  	}
   367  	return s, nil
   368  }