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