github.com/sarguru/terraform@v0.6.17-0.20160525232901-8fcdfd7e3dc9/terraform/state_v1.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/mitchellh/copystructure"
     7  )
     8  
     9  // stateV1 keeps track of a snapshot state-of-the-world that Terraform
    10  // can use to keep track of what real world resources it is actually
    11  // managing.
    12  //
    13  // stateV1 is _only used for the purposes of backwards compatibility
    14  // and is no longer used in Terraform.
    15  type stateV1 struct {
    16  	// Version is the protocol version. "1" for a StateV1.
    17  	Version int `json:"version"`
    18  
    19  	// Serial is incremented on any operation that modifies
    20  	// the State file. It is used to detect potentially conflicting
    21  	// updates.
    22  	Serial int64 `json:"serial"`
    23  
    24  	// Remote is used to track the metadata required to
    25  	// pull and push state files from a remote storage endpoint.
    26  	Remote *remoteStateV1 `json:"remote,omitempty"`
    27  
    28  	// Modules contains all the modules in a breadth-first order
    29  	Modules []*moduleStateV1 `json:"modules"`
    30  }
    31  
    32  // upgrade is used to upgrade a V1 state representation
    33  // into a State (current) representation.
    34  func (old *stateV1) upgrade() (*State, error) {
    35  	if old == nil {
    36  		return nil, nil
    37  	}
    38  
    39  	remote, err := old.Remote.upgrade()
    40  	if err != nil {
    41  		return nil, fmt.Errorf("Error upgrading State V1: %v", err)
    42  	}
    43  
    44  	modules := make([]*ModuleState, len(old.Modules))
    45  	for i, module := range old.Modules {
    46  		upgraded, err := module.upgrade()
    47  		if err != nil {
    48  			return nil, fmt.Errorf("Error upgrading State V1: %v", err)
    49  		}
    50  		modules[i] = upgraded
    51  	}
    52  	if len(modules) == 0 {
    53  		modules = nil
    54  	}
    55  
    56  	newState := &State{
    57  		Version: old.Version,
    58  		Serial:  old.Serial,
    59  		Remote:  remote,
    60  		Modules: modules,
    61  	}
    62  
    63  	newState.sort()
    64  
    65  	return newState, nil
    66  }
    67  
    68  type remoteStateV1 struct {
    69  	// Type controls the client we use for the remote state
    70  	Type string `json:"type"`
    71  
    72  	// Config is used to store arbitrary configuration that
    73  	// is type specific
    74  	Config map[string]string `json:"config"`
    75  }
    76  
    77  func (old *remoteStateV1) upgrade() (*RemoteState, error) {
    78  	if old == nil {
    79  		return nil, nil
    80  	}
    81  
    82  	config, err := copystructure.Copy(old.Config)
    83  	if err != nil {
    84  		return nil, fmt.Errorf("Error upgrading RemoteState V1: %v", err)
    85  	}
    86  
    87  	return &RemoteState{
    88  		Type:   old.Type,
    89  		Config: config.(map[string]string),
    90  	}, nil
    91  }
    92  
    93  type moduleStateV1 struct {
    94  	// Path is the import path from the root module. Modules imports are
    95  	// always disjoint, so the path represents amodule tree
    96  	Path []string `json:"path"`
    97  
    98  	// Outputs declared by the module and maintained for each module
    99  	// even though only the root module technically needs to be kept.
   100  	// This allows operators to inspect values at the boundaries.
   101  	Outputs map[string]string `json:"outputs"`
   102  
   103  	// Resources is a mapping of the logically named resource to
   104  	// the state of the resource. Each resource may actually have
   105  	// N instances underneath, although a user only needs to think
   106  	// about the 1:1 case.
   107  	Resources map[string]*resourceStateV1 `json:"resources"`
   108  
   109  	// Dependencies are a list of things that this module relies on
   110  	// existing to remain intact. For example: an module may depend
   111  	// on a VPC ID given by an aws_vpc resource.
   112  	//
   113  	// Terraform uses this information to build valid destruction
   114  	// orders and to warn the user if they're destroying a module that
   115  	// another resource depends on.
   116  	//
   117  	// Things can be put into this list that may not be managed by
   118  	// Terraform. If Terraform doesn't find a matching ID in the
   119  	// overall state, then it assumes it isn't managed and doesn't
   120  	// worry about it.
   121  	Dependencies []string `json:"depends_on,omitempty"`
   122  }
   123  
   124  func (old *moduleStateV1) upgrade() (*ModuleState, error) {
   125  	if old == nil {
   126  		return nil, nil
   127  	}
   128  
   129  	path, err := copystructure.Copy(old.Path)
   130  	if err != nil {
   131  		return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err)
   132  	}
   133  
   134  	// Outputs needs upgrading to use the new structure
   135  	outputs := make(map[string]*OutputState)
   136  	for key, output := range old.Outputs {
   137  		outputs[key] = &OutputState{
   138  			Type:      "string",
   139  			Value:     output,
   140  			Sensitive: false,
   141  		}
   142  	}
   143  	if len(outputs) == 0 {
   144  		outputs = nil
   145  	}
   146  
   147  	resources := make(map[string]*ResourceState)
   148  	for key, oldResource := range old.Resources {
   149  		upgraded, err := oldResource.upgrade()
   150  		if err != nil {
   151  			return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err)
   152  		}
   153  		resources[key] = upgraded
   154  	}
   155  	if len(resources) == 0 {
   156  		resources = nil
   157  	}
   158  
   159  	dependencies, err := copystructure.Copy(old.Dependencies)
   160  	if err != nil {
   161  		return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err)
   162  	}
   163  
   164  	return &ModuleState{
   165  		Path:         path.([]string),
   166  		Outputs:      outputs,
   167  		Resources:    resources,
   168  		Dependencies: dependencies.([]string),
   169  	}, nil
   170  }
   171  
   172  type resourceStateV1 struct {
   173  	// This is filled in and managed by Terraform, and is the resource
   174  	// type itself such as "mycloud_instance". If a resource provider sets
   175  	// this value, it won't be persisted.
   176  	Type string `json:"type"`
   177  
   178  	// Dependencies are a list of things that this resource relies on
   179  	// existing to remain intact. For example: an AWS instance might
   180  	// depend on a subnet (which itself might depend on a VPC, and so
   181  	// on).
   182  	//
   183  	// Terraform uses this information to build valid destruction
   184  	// orders and to warn the user if they're destroying a resource that
   185  	// another resource depends on.
   186  	//
   187  	// Things can be put into this list that may not be managed by
   188  	// Terraform. If Terraform doesn't find a matching ID in the
   189  	// overall state, then it assumes it isn't managed and doesn't
   190  	// worry about it.
   191  	Dependencies []string `json:"depends_on,omitempty"`
   192  
   193  	// Primary is the current active instance for this resource.
   194  	// It can be replaced but only after a successful creation.
   195  	// This is the instances on which providers will act.
   196  	Primary *instanceStateV1 `json:"primary"`
   197  
   198  	// Tainted is used to track any underlying instances that
   199  	// have been created but are in a bad or unknown state and
   200  	// need to be cleaned up subsequently.  In the
   201  	// standard case, there is only at most a single instance.
   202  	// However, in pathological cases, it is possible for the number
   203  	// of instances to accumulate.
   204  	Tainted []*instanceStateV1 `json:"tainted,omitempty"`
   205  
   206  	// Deposed is used in the mechanics of CreateBeforeDestroy: the existing
   207  	// Primary is Deposed to get it out of the way for the replacement Primary to
   208  	// be created by Apply. If the replacement Primary creates successfully, the
   209  	// Deposed instance is cleaned up. If there were problems creating the
   210  	// replacement, the instance remains in the Deposed list so it can be
   211  	// destroyed in a future run. Functionally, Deposed instances are very
   212  	// similar to Tainted instances in that Terraform is only tracking them in
   213  	// order to remember to destroy them.
   214  	Deposed []*instanceStateV1 `json:"deposed,omitempty"`
   215  
   216  	// Provider is used when a resource is connected to a provider with an alias.
   217  	// If this string is empty, the resource is connected to the default provider,
   218  	// e.g. "aws_instance" goes with the "aws" provider.
   219  	// If the resource block contained a "provider" key, that value will be set here.
   220  	Provider string `json:"provider,omitempty"`
   221  }
   222  
   223  func (old *resourceStateV1) upgrade() (*ResourceState, error) {
   224  	if old == nil {
   225  		return nil, nil
   226  	}
   227  
   228  	dependencies, err := copystructure.Copy(old.Dependencies)
   229  	if err != nil {
   230  		return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
   231  	}
   232  
   233  	primary, err := old.Primary.upgrade()
   234  	if err != nil {
   235  		return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
   236  	}
   237  
   238  	tainted := make([]*InstanceState, len(old.Tainted))
   239  	for i, v := range old.Tainted {
   240  		upgraded, err := v.upgrade()
   241  		if err != nil {
   242  			return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
   243  		}
   244  		tainted[i] = upgraded
   245  	}
   246  	if len(tainted) == 0 {
   247  		tainted = nil
   248  	}
   249  
   250  	deposed := make([]*InstanceState, len(old.Deposed))
   251  	for i, v := range old.Deposed {
   252  		upgraded, err := v.upgrade()
   253  		if err != nil {
   254  			return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
   255  		}
   256  		deposed[i] = upgraded
   257  	}
   258  	if len(deposed) == 0 {
   259  		deposed = nil
   260  	}
   261  
   262  	return &ResourceState{
   263  		Type:         old.Type,
   264  		Dependencies: dependencies.([]string),
   265  		Primary:      primary,
   266  		Tainted:      tainted,
   267  		Deposed:      deposed,
   268  		Provider:     old.Provider,
   269  	}, nil
   270  }
   271  
   272  type instanceStateV1 struct {
   273  	// A unique ID for this resource. This is opaque to Terraform
   274  	// and is only meant as a lookup mechanism for the providers.
   275  	ID string `json:"id"`
   276  
   277  	// Attributes are basic information about the resource. Any keys here
   278  	// are accessible in variable format within Terraform configurations:
   279  	// ${resourcetype.name.attribute}.
   280  	Attributes map[string]string `json:"attributes,omitempty"`
   281  
   282  	// Ephemeral is used to store any state associated with this instance
   283  	// that is necessary for the Terraform run to complete, but is not
   284  	// persisted to a state file.
   285  	Ephemeral ephemeralStateV1 `json:"-"`
   286  
   287  	// Meta is a simple K/V map that is persisted to the State but otherwise
   288  	// ignored by Terraform core. It's meant to be used for accounting by
   289  	// external client code.
   290  	Meta map[string]string `json:"meta,omitempty"`
   291  }
   292  
   293  func (old *instanceStateV1) upgrade() (*InstanceState, error) {
   294  	if old == nil {
   295  		return nil, nil
   296  	}
   297  
   298  	attributes, err := copystructure.Copy(old.Attributes)
   299  	if err != nil {
   300  		return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err)
   301  	}
   302  	ephemeral, err := old.Ephemeral.upgrade()
   303  	if err != nil {
   304  		return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err)
   305  	}
   306  	meta, err := copystructure.Copy(old.Meta)
   307  	if err != nil {
   308  		return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err)
   309  	}
   310  
   311  	return &InstanceState{
   312  		ID:         old.ID,
   313  		Attributes: attributes.(map[string]string),
   314  		Ephemeral:  *ephemeral,
   315  		Meta:       meta.(map[string]string),
   316  	}, nil
   317  }
   318  
   319  type ephemeralStateV1 struct {
   320  	// ConnInfo is used for the providers to export information which is
   321  	// used to connect to the resource for provisioning. For example,
   322  	// this could contain SSH or WinRM credentials.
   323  	ConnInfo map[string]string `json:"-"`
   324  }
   325  
   326  func (old *ephemeralStateV1) upgrade() (*EphemeralState, error) {
   327  	connInfo, err := copystructure.Copy(old.ConnInfo)
   328  	if err != nil {
   329  		return nil, fmt.Errorf("Error upgrading EphemeralState V1: %v", err)
   330  	}
   331  	return &EphemeralState{
   332  		ConnInfo: connInfo.(map[string]string),
   333  	}, nil
   334  }