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

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