github.com/biggiemac/terraform@v0.6.12-0.20160217180759-34b7665af0d6/terraform/graph_config_node_resource.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/terraform/config"
     8  	"github.com/hashicorp/terraform/dag"
     9  	"github.com/hashicorp/terraform/dot"
    10  )
    11  
    12  // GraphNodeCountDependent is implemented by resources for giving only
    13  // the dependencies they have from the "count" field.
    14  type GraphNodeCountDependent interface {
    15  	CountDependentOn() []string
    16  }
    17  
    18  // GraphNodeConfigResource represents a resource within the config graph.
    19  type GraphNodeConfigResource struct {
    20  	Resource *config.Resource
    21  
    22  	// If this is set to anything other than destroyModeNone, then this
    23  	// resource represents a resource that will be destroyed in some way.
    24  	DestroyMode GraphNodeDestroyMode
    25  
    26  	// Used during DynamicExpand to target indexes
    27  	Targets []ResourceAddress
    28  
    29  	Path []string
    30  }
    31  
    32  func (n *GraphNodeConfigResource) Copy() *GraphNodeConfigResource {
    33  	ncr := &GraphNodeConfigResource{
    34  		Resource:    n.Resource.Copy(),
    35  		DestroyMode: n.DestroyMode,
    36  		Targets:     make([]ResourceAddress, 0, len(n.Targets)),
    37  		Path:        make([]string, 0, len(n.Path)),
    38  	}
    39  	for _, t := range n.Targets {
    40  		ncr.Targets = append(ncr.Targets, *t.Copy())
    41  	}
    42  	for _, p := range n.Path {
    43  		ncr.Path = append(ncr.Path, p)
    44  	}
    45  	return ncr
    46  }
    47  
    48  func (n *GraphNodeConfigResource) ConfigType() GraphNodeConfigType {
    49  	return GraphNodeConfigTypeResource
    50  }
    51  
    52  func (n *GraphNodeConfigResource) DependableName() []string {
    53  	return []string{n.Resource.Id()}
    54  }
    55  
    56  // GraphNodeCountDependent impl.
    57  func (n *GraphNodeConfigResource) CountDependentOn() []string {
    58  	result := make([]string, 0, len(n.Resource.RawCount.Variables))
    59  	for _, v := range n.Resource.RawCount.Variables {
    60  		if vn := varNameForVar(v); vn != "" {
    61  			result = append(result, vn)
    62  		}
    63  	}
    64  
    65  	return result
    66  }
    67  
    68  // GraphNodeDependent impl.
    69  func (n *GraphNodeConfigResource) DependentOn() []string {
    70  	result := make([]string, len(n.Resource.DependsOn),
    71  		(len(n.Resource.RawCount.Variables)+
    72  			len(n.Resource.RawConfig.Variables)+
    73  			len(n.Resource.DependsOn))*2)
    74  	copy(result, n.Resource.DependsOn)
    75  
    76  	for _, v := range n.Resource.RawCount.Variables {
    77  		if vn := varNameForVar(v); vn != "" {
    78  			result = append(result, vn)
    79  		}
    80  	}
    81  	for _, v := range n.Resource.RawConfig.Variables {
    82  		if vn := varNameForVar(v); vn != "" {
    83  			result = append(result, vn)
    84  		}
    85  	}
    86  	for _, p := range n.Resource.Provisioners {
    87  		for _, v := range p.ConnInfo.Variables {
    88  			if vn := varNameForVar(v); vn != "" && vn != n.Resource.Id() {
    89  				result = append(result, vn)
    90  			}
    91  		}
    92  		for _, v := range p.RawConfig.Variables {
    93  			if vn := varNameForVar(v); vn != "" && vn != n.Resource.Id() {
    94  				result = append(result, vn)
    95  			}
    96  		}
    97  	}
    98  
    99  	return result
   100  }
   101  
   102  // VarWalk calls a callback for all the variables that this resource
   103  // depends on.
   104  func (n *GraphNodeConfigResource) VarWalk(fn func(config.InterpolatedVariable)) {
   105  	for _, v := range n.Resource.RawCount.Variables {
   106  		fn(v)
   107  	}
   108  	for _, v := range n.Resource.RawConfig.Variables {
   109  		fn(v)
   110  	}
   111  	for _, p := range n.Resource.Provisioners {
   112  		for _, v := range p.ConnInfo.Variables {
   113  			fn(v)
   114  		}
   115  		for _, v := range p.RawConfig.Variables {
   116  			fn(v)
   117  		}
   118  	}
   119  }
   120  
   121  func (n *GraphNodeConfigResource) Name() string {
   122  	result := n.Resource.Id()
   123  	switch n.DestroyMode {
   124  	case DestroyNone:
   125  	case DestroyPrimary:
   126  		result += " (destroy)"
   127  	case DestroyTainted:
   128  		result += " (destroy tainted)"
   129  	default:
   130  		result += " (unknown destroy type)"
   131  	}
   132  
   133  	return result
   134  }
   135  
   136  // GraphNodeDotter impl.
   137  func (n *GraphNodeConfigResource) DotNode(name string, opts *GraphDotOpts) *dot.Node {
   138  	if n.DestroyMode != DestroyNone && !opts.Verbose {
   139  		return nil
   140  	}
   141  	return dot.NewNode(name, map[string]string{
   142  		"label": n.Name(),
   143  		"shape": "box",
   144  	})
   145  }
   146  
   147  // GraphNodeFlattenable impl.
   148  func (n *GraphNodeConfigResource) Flatten(p []string) (dag.Vertex, error) {
   149  	return &GraphNodeConfigResourceFlat{
   150  		GraphNodeConfigResource: n,
   151  		PathValue:               p,
   152  	}, nil
   153  }
   154  
   155  // GraphNodeDynamicExpandable impl.
   156  func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
   157  	state, lock := ctx.State()
   158  	lock.RLock()
   159  	defer lock.RUnlock()
   160  
   161  	// Start creating the steps
   162  	steps := make([]GraphTransformer, 0, 5)
   163  
   164  	// Primary and non-destroy modes are responsible for creating/destroying
   165  	// all the nodes, expanding counts.
   166  	switch n.DestroyMode {
   167  	case DestroyNone, DestroyPrimary:
   168  		steps = append(steps, &ResourceCountTransformer{
   169  			Resource: n.Resource,
   170  			Destroy:  n.DestroyMode != DestroyNone,
   171  			Targets:  n.Targets,
   172  		})
   173  	}
   174  
   175  	// Additional destroy modifications.
   176  	switch n.DestroyMode {
   177  	case DestroyPrimary:
   178  		// If we're destroying the primary instance, then we want to
   179  		// expand orphans, which have all the same semantics in a destroy
   180  		// as a primary.
   181  		steps = append(steps, &OrphanTransformer{
   182  			State: state,
   183  			View:  n.Resource.Id(),
   184  		})
   185  
   186  		steps = append(steps, &DeposedTransformer{
   187  			State: state,
   188  			View:  n.Resource.Id(),
   189  		})
   190  	case DestroyTainted:
   191  		// If we're only destroying tainted resources, then we only
   192  		// want to find tainted resources and destroy them here.
   193  		steps = append(steps, &TaintedTransformer{
   194  			State: state,
   195  			View:  n.Resource.Id(),
   196  		})
   197  	}
   198  
   199  	// We always want to apply targeting
   200  	steps = append(steps, &TargetsTransformer{
   201  		ParsedTargets: n.Targets,
   202  		Destroy:       n.DestroyMode != DestroyNone,
   203  	})
   204  
   205  	// Always end with the root being added
   206  	steps = append(steps, &RootTransformer{})
   207  
   208  	// Build the graph
   209  	b := &BasicGraphBuilder{Steps: steps}
   210  	return b.Build(ctx.Path())
   211  }
   212  
   213  // GraphNodeAddressable impl.
   214  func (n *GraphNodeConfigResource) ResourceAddress() *ResourceAddress {
   215  	return &ResourceAddress{
   216  		Path:         n.Path[1:],
   217  		Index:        -1,
   218  		InstanceType: TypePrimary,
   219  		Name:         n.Resource.Name,
   220  		Type:         n.Resource.Type,
   221  	}
   222  }
   223  
   224  // GraphNodeTargetable impl.
   225  func (n *GraphNodeConfigResource) SetTargets(targets []ResourceAddress) {
   226  	n.Targets = targets
   227  }
   228  
   229  // GraphNodeEvalable impl.
   230  func (n *GraphNodeConfigResource) EvalTree() EvalNode {
   231  	return &EvalSequence{
   232  		Nodes: []EvalNode{
   233  			&EvalInterpolate{Config: n.Resource.RawCount},
   234  			&EvalOpFilter{
   235  				Ops:  []walkOperation{walkValidate},
   236  				Node: &EvalValidateCount{Resource: n.Resource},
   237  			},
   238  			&EvalCountFixZeroOneBoundary{Resource: n.Resource},
   239  		},
   240  	}
   241  }
   242  
   243  // GraphNodeProviderConsumer
   244  func (n *GraphNodeConfigResource) ProvidedBy() []string {
   245  	return []string{resourceProvider(n.Resource.Type, n.Resource.Provider)}
   246  }
   247  
   248  // GraphNodeProvisionerConsumer
   249  func (n *GraphNodeConfigResource) ProvisionedBy() []string {
   250  	result := make([]string, len(n.Resource.Provisioners))
   251  	for i, p := range n.Resource.Provisioners {
   252  		result[i] = p.Type
   253  	}
   254  
   255  	return result
   256  }
   257  
   258  // GraphNodeDestroyable
   259  func (n *GraphNodeConfigResource) DestroyNode(mode GraphNodeDestroyMode) GraphNodeDestroy {
   260  	// If we're already a destroy node, then don't do anything
   261  	if n.DestroyMode != DestroyNone {
   262  		return nil
   263  	}
   264  
   265  	result := &graphNodeResourceDestroy{
   266  		GraphNodeConfigResource: *n.Copy(),
   267  		Original:                n,
   268  	}
   269  	result.DestroyMode = mode
   270  	return result
   271  }
   272  
   273  // GraphNodeNoopPrunable
   274  func (n *GraphNodeConfigResource) Noop(opts *NoopOpts) bool {
   275  	// We don't have any noop optimizations for destroy nodes yet
   276  	if n.DestroyMode != DestroyNone {
   277  		return false
   278  	}
   279  
   280  	// If there is no diff, then we aren't a noop since something needs to
   281  	// be done (such as a plan). We only check if we're a noop in a diff.
   282  	if opts.Diff == nil || opts.Diff.Empty() {
   283  		return false
   284  	}
   285  
   286  	// If we have no module diff, we're certainly a noop. This is because
   287  	// it means there is a diff, and that the module we're in just isn't
   288  	// in it, meaning we're not doing anything.
   289  	if opts.ModDiff == nil || opts.ModDiff.Empty() {
   290  		return true
   291  	}
   292  
   293  	// Grab the ID which is the prefix (in the case count > 0 at some point)
   294  	prefix := n.Resource.Id()
   295  
   296  	// Go through the diff and if there are any with our name on it, keep us
   297  	found := false
   298  	for k, _ := range opts.ModDiff.Resources {
   299  		if strings.HasPrefix(k, prefix) {
   300  			found = true
   301  			break
   302  		}
   303  	}
   304  
   305  	return !found
   306  }
   307  
   308  // Same as GraphNodeConfigResource, but for flattening
   309  type GraphNodeConfigResourceFlat struct {
   310  	*GraphNodeConfigResource
   311  
   312  	PathValue []string
   313  }
   314  
   315  func (n *GraphNodeConfigResourceFlat) Name() string {
   316  	return fmt.Sprintf(
   317  		"%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigResource.Name())
   318  }
   319  
   320  func (n *GraphNodeConfigResourceFlat) Path() []string {
   321  	return n.PathValue
   322  }
   323  
   324  func (n *GraphNodeConfigResourceFlat) DependableName() []string {
   325  	return modulePrefixList(
   326  		n.GraphNodeConfigResource.DependableName(),
   327  		modulePrefixStr(n.PathValue))
   328  }
   329  
   330  func (n *GraphNodeConfigResourceFlat) DependentOn() []string {
   331  	prefix := modulePrefixStr(n.PathValue)
   332  	return modulePrefixList(
   333  		n.GraphNodeConfigResource.DependentOn(),
   334  		prefix)
   335  }
   336  
   337  func (n *GraphNodeConfigResourceFlat) ProvidedBy() []string {
   338  	prefix := modulePrefixStr(n.PathValue)
   339  	return modulePrefixList(
   340  		n.GraphNodeConfigResource.ProvidedBy(),
   341  		prefix)
   342  }
   343  
   344  func (n *GraphNodeConfigResourceFlat) ProvisionedBy() []string {
   345  	prefix := modulePrefixStr(n.PathValue)
   346  	return modulePrefixList(
   347  		n.GraphNodeConfigResource.ProvisionedBy(),
   348  		prefix)
   349  }
   350  
   351  // GraphNodeDestroyable impl.
   352  func (n *GraphNodeConfigResourceFlat) DestroyNode(mode GraphNodeDestroyMode) GraphNodeDestroy {
   353  	// Get our parent destroy node. If we don't have any, just return
   354  	raw := n.GraphNodeConfigResource.DestroyNode(mode)
   355  	if raw == nil {
   356  		return nil
   357  	}
   358  
   359  	node, ok := raw.(*graphNodeResourceDestroy)
   360  	if !ok {
   361  		panic(fmt.Sprintf("unknown destroy node: %s %T", dag.VertexName(raw), raw))
   362  	}
   363  
   364  	// Otherwise, wrap it so that it gets the proper module treatment.
   365  	return &graphNodeResourceDestroyFlat{
   366  		graphNodeResourceDestroy: node,
   367  		PathValue:                n.PathValue,
   368  		FlatCreateNode:           n,
   369  	}
   370  }
   371  
   372  type graphNodeResourceDestroyFlat struct {
   373  	*graphNodeResourceDestroy
   374  
   375  	PathValue []string
   376  
   377  	// Needs to be able to properly yield back a flattened create node to prevent
   378  	FlatCreateNode *GraphNodeConfigResourceFlat
   379  }
   380  
   381  func (n *graphNodeResourceDestroyFlat) Name() string {
   382  	return fmt.Sprintf(
   383  		"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeResourceDestroy.Name())
   384  }
   385  
   386  func (n *graphNodeResourceDestroyFlat) Path() []string {
   387  	return n.PathValue
   388  }
   389  
   390  func (n *graphNodeResourceDestroyFlat) CreateNode() dag.Vertex {
   391  	return n.FlatCreateNode
   392  }
   393  
   394  func (n *graphNodeResourceDestroyFlat) ProvidedBy() []string {
   395  	prefix := modulePrefixStr(n.PathValue)
   396  	return modulePrefixList(
   397  		n.GraphNodeConfigResource.ProvidedBy(),
   398  		prefix)
   399  }
   400  
   401  // graphNodeResourceDestroy represents the logical destruction of a
   402  // resource. This node doesn't mean it will be destroyed for sure, but
   403  // instead that if a destroy were to happen, it must happen at this point.
   404  type graphNodeResourceDestroy struct {
   405  	GraphNodeConfigResource
   406  	Original *GraphNodeConfigResource
   407  }
   408  
   409  func (n *graphNodeResourceDestroy) CreateBeforeDestroy() bool {
   410  	// CBD is enabled if the resource enables it in addition to us
   411  	// being responsible for destroying the primary state. The primary
   412  	// state destroy node is the only destroy node that needs to be
   413  	// "shuffled" according to the CBD rules, since tainted resources
   414  	// don't have the same inverse dependencies.
   415  	return n.Original.Resource.Lifecycle.CreateBeforeDestroy &&
   416  		n.DestroyMode == DestroyPrimary
   417  }
   418  
   419  func (n *graphNodeResourceDestroy) CreateNode() dag.Vertex {
   420  	return n.Original
   421  }
   422  
   423  func (n *graphNodeResourceDestroy) DestroyInclude(d *ModuleDiff, s *ModuleState) bool {
   424  	switch n.DestroyMode {
   425  	case DestroyPrimary:
   426  		return n.destroyIncludePrimary(d, s)
   427  	case DestroyTainted:
   428  		return n.destroyIncludeTainted(d, s)
   429  	default:
   430  		return true
   431  	}
   432  }
   433  
   434  func (n *graphNodeResourceDestroy) destroyIncludeTainted(
   435  	d *ModuleDiff, s *ModuleState) bool {
   436  	// If there is no state, there can't by any tainted.
   437  	if s == nil {
   438  		return false
   439  	}
   440  
   441  	// Grab the ID which is the prefix (in the case count > 0 at some point)
   442  	prefix := n.Original.Resource.Id()
   443  
   444  	// Go through the resources and find any with our prefix. If there
   445  	// are any tainted, we need to keep it.
   446  	for k, v := range s.Resources {
   447  		if !strings.HasPrefix(k, prefix) {
   448  			continue
   449  		}
   450  
   451  		if len(v.Tainted) > 0 {
   452  			return true
   453  		}
   454  	}
   455  
   456  	// We didn't find any tainted nodes, return
   457  	return false
   458  }
   459  
   460  func (n *graphNodeResourceDestroy) destroyIncludePrimary(
   461  	d *ModuleDiff, s *ModuleState) bool {
   462  	// Get the count, and specifically the raw value of the count
   463  	// (with interpolations and all). If the count is NOT a static "1",
   464  	// then we keep the destroy node no matter what.
   465  	//
   466  	// The reasoning for this is complicated and not intuitively obvious,
   467  	// but I attempt to explain it below.
   468  	//
   469  	// The destroy transform works by generating the worst case graph,
   470  	// with worst case being the case that every resource already exists
   471  	// and needs to be destroy/created (force-new). There is a single important
   472  	// edge case where this actually results in a real-life cycle: if a
   473  	// create-before-destroy (CBD) resource depends on a non-CBD resource.
   474  	// Imagine a EC2 instance "foo" with CBD depending on a security
   475  	// group "bar" without CBD, and conceptualize the worst case destroy
   476  	// order:
   477  	//
   478  	//   1.) SG must be destroyed (non-CBD)
   479  	//   2.) SG must be created/updated
   480  	//   3.) EC2 instance must be created (CBD, requires the SG be made)
   481  	//   4.) EC2 instance must be destroyed (requires SG be destroyed)
   482  	//
   483  	// Except, #1 depends on #4, since the SG can't be destroyed while
   484  	// an EC2 instance is using it (AWS API requirements). As you can see,
   485  	// this is a real life cycle that can't be automatically reconciled
   486  	// except under two conditions:
   487  	//
   488  	//   1.) SG is also CBD. This doesn't work 100% of the time though
   489  	//       since the non-CBD resource might not support CBD. To make matters
   490  	//       worse, the entire transitive closure of dependencies must be
   491  	//       CBD (if the SG depends on a VPC, you have the same problem).
   492  	//   2.) EC2 must not CBD. This can't happen automatically because CBD
   493  	//       is used as a way to ensure zero (or minimal) downtime Terraform
   494  	//       applies, and it isn't acceptable for TF to ignore this request,
   495  	//       since it can result in unexpected downtime.
   496  	//
   497  	// Therefore, we compromise with this edge case here: if there is
   498  	// a static count of "1", we prune the diff to remove cycles during a
   499  	// graph optimization path if we don't see the resource in the diff.
   500  	// If the count is set to ANYTHING other than a static "1" (variable,
   501  	// computed attribute, static number greater than 1), then we keep the
   502  	// destroy, since it is required for dynamic graph expansion to find
   503  	// orphan/tainted count objects.
   504  	//
   505  	// This isn't ideal logic, but its strictly better without introducing
   506  	// new impossibilities. It breaks the cycle in practical cases, and the
   507  	// cycle comes back in no cases we've found to be practical, but just
   508  	// as the cycle would already exist without this anyways.
   509  	count := n.Original.Resource.RawCount
   510  	if raw := count.Raw[count.Key]; raw != "1" {
   511  		return true
   512  	}
   513  
   514  	// Okay, we're dealing with a static count. There are a few ways
   515  	// to include this resource.
   516  	prefix := n.Original.Resource.Id()
   517  
   518  	// If we're present in the diff proper, then keep it. We're looking
   519  	// only for resources in the diff that match our resource or a count-index
   520  	// of our resource that are marked for destroy.
   521  	if d != nil {
   522  		for k, d := range d.Resources {
   523  			match := k == prefix || strings.HasPrefix(k, prefix+".")
   524  			if match && d.Destroy {
   525  				return true
   526  			}
   527  		}
   528  	}
   529  
   530  	// If we're in the state as a primary in any form, then keep it.
   531  	// This does a prefix check so it will also catch orphans on count
   532  	// decreases to "1".
   533  	if s != nil {
   534  		for k, v := range s.Resources {
   535  			// Ignore exact matches
   536  			if k == prefix {
   537  				continue
   538  			}
   539  
   540  			// Ignore anything that doesn't have a "." afterwards so that
   541  			// we only get our own resource and any counts on it.
   542  			if !strings.HasPrefix(k, prefix+".") {
   543  				continue
   544  			}
   545  
   546  			// Ignore exact matches and the 0'th index. We only care
   547  			// about if there is a decrease in count.
   548  			if k == prefix+".0" {
   549  				continue
   550  			}
   551  
   552  			if v.Primary != nil {
   553  				return true
   554  			}
   555  		}
   556  
   557  		// If we're in the state as _both_ "foo" and "foo.0", then
   558  		// keep it, since we treat the latter as an orphan.
   559  		_, okOne := s.Resources[prefix]
   560  		_, okTwo := s.Resources[prefix+".0"]
   561  		if okOne && okTwo {
   562  			return true
   563  		}
   564  	}
   565  
   566  	return false
   567  }