github.com/r3labs/terraform@v0.8.4/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  )
    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 set to true, this resource represents a resource
    23  	// that will be destroyed in some way.
    24  	Destroy bool
    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  		Destroy:  n.Destroy,
    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  	if n.Destroy {
   124  		result += " (destroy)"
   125  	}
   126  	return result
   127  }
   128  
   129  // GraphNodeDotter impl.
   130  func (n *GraphNodeConfigResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
   131  	if n.Destroy && !opts.Verbose {
   132  		return nil
   133  	}
   134  	return &dag.DotNode{
   135  		Name: name,
   136  		Attrs: map[string]string{
   137  			"label": n.Name(),
   138  			"shape": "box",
   139  		},
   140  	}
   141  }
   142  
   143  // GraphNodeFlattenable impl.
   144  func (n *GraphNodeConfigResource) Flatten(p []string) (dag.Vertex, error) {
   145  	return &GraphNodeConfigResourceFlat{
   146  		GraphNodeConfigResource: n,
   147  		PathValue:               p,
   148  	}, nil
   149  }
   150  
   151  // GraphNodeDynamicExpandable impl.
   152  func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
   153  	state, lock := ctx.State()
   154  	lock.RLock()
   155  	defer lock.RUnlock()
   156  
   157  	// Start creating the steps
   158  	steps := make([]GraphTransformer, 0, 5)
   159  
   160  	// Expand counts.
   161  	steps = append(steps, &ResourceCountTransformerOld{
   162  		Resource: n.Resource,
   163  		Destroy:  n.Destroy,
   164  		Targets:  n.Targets,
   165  	})
   166  
   167  	// Additional destroy modifications.
   168  	if n.Destroy {
   169  		// If we're destroying a primary or tainted resource, we want to
   170  		// expand orphans, which have all the same semantics in a destroy
   171  		// as a primary or tainted resource.
   172  		steps = append(steps, &OrphanTransformer{
   173  			Resource: n.Resource,
   174  			State:    state,
   175  			View:     n.Resource.Id(),
   176  		})
   177  
   178  		steps = append(steps, &DeposedTransformer{
   179  			State: state,
   180  			View:  n.Resource.Id(),
   181  		})
   182  	}
   183  
   184  	// We always want to apply targeting
   185  	steps = append(steps, &TargetsTransformer{
   186  		ParsedTargets: n.Targets,
   187  		Destroy:       n.Destroy,
   188  	})
   189  
   190  	// Always end with the root being added
   191  	steps = append(steps, &RootTransformer{})
   192  
   193  	// Build the graph
   194  	b := &BasicGraphBuilder{
   195  		Steps:    steps,
   196  		Validate: true,
   197  		Name:     "GraphNodeConfigResource",
   198  	}
   199  	return b.Build(ctx.Path())
   200  }
   201  
   202  // GraphNodeAddressable impl.
   203  func (n *GraphNodeConfigResource) ResourceAddress() *ResourceAddress {
   204  	return &ResourceAddress{
   205  		Path:         n.Path[1:],
   206  		Index:        -1,
   207  		InstanceType: TypePrimary,
   208  		Name:         n.Resource.Name,
   209  		Type:         n.Resource.Type,
   210  		Mode:         n.Resource.Mode,
   211  	}
   212  }
   213  
   214  // GraphNodeTargetable impl.
   215  func (n *GraphNodeConfigResource) SetTargets(targets []ResourceAddress) {
   216  	n.Targets = targets
   217  }
   218  
   219  // GraphNodeEvalable impl.
   220  func (n *GraphNodeConfigResource) EvalTree() EvalNode {
   221  	return &EvalSequence{
   222  		Nodes: []EvalNode{
   223  			&EvalInterpolate{Config: n.Resource.RawCount},
   224  			&EvalCountCheckComputed{Resource: n.Resource},
   225  			&EvalOpFilter{
   226  				Ops:  []walkOperation{walkValidate},
   227  				Node: &EvalValidateCount{Resource: n.Resource},
   228  			},
   229  			&EvalCountFixZeroOneBoundary{Resource: n.Resource},
   230  		},
   231  	}
   232  }
   233  
   234  // GraphNodeProviderConsumer
   235  func (n *GraphNodeConfigResource) ProvidedBy() []string {
   236  	return []string{resourceProvider(n.Resource.Type, n.Resource.Provider)}
   237  }
   238  
   239  // GraphNodeProvisionerConsumer
   240  func (n *GraphNodeConfigResource) ProvisionedBy() []string {
   241  	result := make([]string, len(n.Resource.Provisioners))
   242  	for i, p := range n.Resource.Provisioners {
   243  		result[i] = p.Type
   244  	}
   245  
   246  	return result
   247  }
   248  
   249  // GraphNodeDestroyable
   250  func (n *GraphNodeConfigResource) DestroyNode() GraphNodeDestroy {
   251  	// If we're already a destroy node, then don't do anything
   252  	if n.Destroy {
   253  		return nil
   254  	}
   255  
   256  	result := &graphNodeResourceDestroy{
   257  		GraphNodeConfigResource: *n.Copy(),
   258  		Original:                n,
   259  	}
   260  	result.Destroy = true
   261  
   262  	return result
   263  }
   264  
   265  // GraphNodeNoopPrunable
   266  func (n *GraphNodeConfigResource) Noop(opts *NoopOpts) bool {
   267  	log.Printf("[DEBUG] Checking resource noop: %s", n.Name())
   268  	// We don't have any noop optimizations for destroy nodes yet
   269  	if n.Destroy {
   270  		log.Printf("[DEBUG] Destroy node, not a noop")
   271  		return false
   272  	}
   273  
   274  	// If there is no diff, then we aren't a noop since something needs to
   275  	// be done (such as a plan). We only check if we're a noop in a diff.
   276  	if opts.Diff == nil || opts.Diff.Empty() {
   277  		log.Printf("[DEBUG] No diff, not a noop")
   278  		return false
   279  	}
   280  
   281  	// If the count has any interpolations, we can't prune this node since
   282  	// we need to be sure to evaluate the count so that splat variables work
   283  	// later (which need to know the full count).
   284  	if len(n.Resource.RawCount.Interpolations) > 0 {
   285  		log.Printf("[DEBUG] Count has interpolations, not a noop")
   286  		return false
   287  	}
   288  
   289  	// If we have no module diff, we're certainly a noop. This is because
   290  	// it means there is a diff, and that the module we're in just isn't
   291  	// in it, meaning we're not doing anything.
   292  	if opts.ModDiff == nil || opts.ModDiff.Empty() {
   293  		log.Printf("[DEBUG] No mod diff, treating resource as a noop")
   294  		return true
   295  	}
   296  
   297  	// Grab the ID which is the prefix (in the case count > 0 at some point)
   298  	prefix := n.Resource.Id()
   299  
   300  	// Go through the diff and if there are any with our name on it, keep us
   301  	found := false
   302  	for k, _ := range opts.ModDiff.Resources {
   303  		if strings.HasPrefix(k, prefix) {
   304  			log.Printf("[DEBUG] Diff has %s, resource is not a noop", k)
   305  			found = true
   306  			break
   307  		}
   308  	}
   309  
   310  	log.Printf("[DEBUG] Final noop value: %t", !found)
   311  	return !found
   312  }
   313  
   314  // Same as GraphNodeConfigResource, but for flattening
   315  type GraphNodeConfigResourceFlat struct {
   316  	*GraphNodeConfigResource
   317  
   318  	PathValue []string
   319  }
   320  
   321  func (n *GraphNodeConfigResourceFlat) Name() string {
   322  	return fmt.Sprintf(
   323  		"%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigResource.Name())
   324  }
   325  
   326  func (n *GraphNodeConfigResourceFlat) Path() []string {
   327  	return n.PathValue
   328  }
   329  
   330  func (n *GraphNodeConfigResourceFlat) DependableName() []string {
   331  	return modulePrefixList(
   332  		n.GraphNodeConfigResource.DependableName(),
   333  		modulePrefixStr(n.PathValue))
   334  }
   335  
   336  func (n *GraphNodeConfigResourceFlat) DependentOn() []string {
   337  	prefix := modulePrefixStr(n.PathValue)
   338  	return modulePrefixList(
   339  		n.GraphNodeConfigResource.DependentOn(),
   340  		prefix)
   341  }
   342  
   343  func (n *GraphNodeConfigResourceFlat) ProvidedBy() []string {
   344  	prefix := modulePrefixStr(n.PathValue)
   345  	return modulePrefixList(
   346  		n.GraphNodeConfigResource.ProvidedBy(),
   347  		prefix)
   348  }
   349  
   350  func (n *GraphNodeConfigResourceFlat) ProvisionedBy() []string {
   351  	prefix := modulePrefixStr(n.PathValue)
   352  	return modulePrefixList(
   353  		n.GraphNodeConfigResource.ProvisionedBy(),
   354  		prefix)
   355  }
   356  
   357  // GraphNodeDestroyable impl.
   358  func (n *GraphNodeConfigResourceFlat) DestroyNode() GraphNodeDestroy {
   359  	// Get our parent destroy node. If we don't have any, just return
   360  	raw := n.GraphNodeConfigResource.DestroyNode()
   361  	if raw == nil {
   362  		return nil
   363  	}
   364  
   365  	node, ok := raw.(*graphNodeResourceDestroy)
   366  	if !ok {
   367  		panic(fmt.Sprintf("unknown destroy node: %s %T", dag.VertexName(raw), raw))
   368  	}
   369  
   370  	// Otherwise, wrap it so that it gets the proper module treatment.
   371  	return &graphNodeResourceDestroyFlat{
   372  		graphNodeResourceDestroy: node,
   373  		PathValue:                n.PathValue,
   374  		FlatCreateNode:           n,
   375  	}
   376  }
   377  
   378  type graphNodeResourceDestroyFlat struct {
   379  	*graphNodeResourceDestroy
   380  
   381  	PathValue []string
   382  
   383  	// Needs to be able to properly yield back a flattened create node to prevent
   384  	FlatCreateNode *GraphNodeConfigResourceFlat
   385  }
   386  
   387  func (n *graphNodeResourceDestroyFlat) Name() string {
   388  	return fmt.Sprintf(
   389  		"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeResourceDestroy.Name())
   390  }
   391  
   392  func (n *graphNodeResourceDestroyFlat) Path() []string {
   393  	return n.PathValue
   394  }
   395  
   396  func (n *graphNodeResourceDestroyFlat) CreateNode() dag.Vertex {
   397  	return n.FlatCreateNode
   398  }
   399  
   400  func (n *graphNodeResourceDestroyFlat) ProvidedBy() []string {
   401  	prefix := modulePrefixStr(n.PathValue)
   402  	return modulePrefixList(
   403  		n.GraphNodeConfigResource.ProvidedBy(),
   404  		prefix)
   405  }
   406  
   407  // graphNodeResourceDestroy represents the logical destruction of a
   408  // resource. This node doesn't mean it will be destroyed for sure, but
   409  // instead that if a destroy were to happen, it must happen at this point.
   410  type graphNodeResourceDestroy struct {
   411  	GraphNodeConfigResource
   412  	Original *GraphNodeConfigResource
   413  }
   414  
   415  func (n *graphNodeResourceDestroy) CreateBeforeDestroy() bool {
   416  	// CBD is enabled if the resource enables it
   417  	return n.Original.Resource.Lifecycle.CreateBeforeDestroy && n.Destroy
   418  }
   419  
   420  func (n *graphNodeResourceDestroy) CreateNode() dag.Vertex {
   421  	return n.Original
   422  }
   423  
   424  func (n *graphNodeResourceDestroy) DestroyInclude(d *ModuleDiff, s *ModuleState) bool {
   425  	if n.Destroy {
   426  		return n.destroyInclude(d, s)
   427  	}
   428  
   429  	return true
   430  }
   431  
   432  func (n *graphNodeResourceDestroy) destroyInclude(
   433  	d *ModuleDiff, s *ModuleState) bool {
   434  	// Get the count, and specifically the raw value of the count
   435  	// (with interpolations and all). If the count is NOT a static "1",
   436  	// then we keep the destroy node no matter what.
   437  	//
   438  	// The reasoning for this is complicated and not intuitively obvious,
   439  	// but I attempt to explain it below.
   440  	//
   441  	// The destroy transform works by generating the worst case graph,
   442  	// with worst case being the case that every resource already exists
   443  	// and needs to be destroy/created (force-new). There is a single important
   444  	// edge case where this actually results in a real-life cycle: if a
   445  	// create-before-destroy (CBD) resource depends on a non-CBD resource.
   446  	// Imagine a EC2 instance "foo" with CBD depending on a security
   447  	// group "bar" without CBD, and conceptualize the worst case destroy
   448  	// order:
   449  	//
   450  	//   1.) SG must be destroyed (non-CBD)
   451  	//   2.) SG must be created/updated
   452  	//   3.) EC2 instance must be created (CBD, requires the SG be made)
   453  	//   4.) EC2 instance must be destroyed (requires SG be destroyed)
   454  	//
   455  	// Except, #1 depends on #4, since the SG can't be destroyed while
   456  	// an EC2 instance is using it (AWS API requirements). As you can see,
   457  	// this is a real life cycle that can't be automatically reconciled
   458  	// except under two conditions:
   459  	//
   460  	//   1.) SG is also CBD. This doesn't work 100% of the time though
   461  	//       since the non-CBD resource might not support CBD. To make matters
   462  	//       worse, the entire transitive closure of dependencies must be
   463  	//       CBD (if the SG depends on a VPC, you have the same problem).
   464  	//   2.) EC2 must not CBD. This can't happen automatically because CBD
   465  	//       is used as a way to ensure zero (or minimal) downtime Terraform
   466  	//       applies, and it isn't acceptable for TF to ignore this request,
   467  	//       since it can result in unexpected downtime.
   468  	//
   469  	// Therefore, we compromise with this edge case here: if there is
   470  	// a static count of "1", we prune the diff to remove cycles during a
   471  	// graph optimization path if we don't see the resource in the diff.
   472  	// If the count is set to ANYTHING other than a static "1" (variable,
   473  	// computed attribute, static number greater than 1), then we keep the
   474  	// destroy, since it is required for dynamic graph expansion to find
   475  	// orphan count objects.
   476  	//
   477  	// This isn't ideal logic, but its strictly better without introducing
   478  	// new impossibilities. It breaks the cycle in practical cases, and the
   479  	// cycle comes back in no cases we've found to be practical, but just
   480  	// as the cycle would already exist without this anyways.
   481  	count := n.Original.Resource.RawCount
   482  	if raw := count.Raw[count.Key]; raw != "1" {
   483  		return true
   484  	}
   485  
   486  	// Okay, we're dealing with a static count. There are a few ways
   487  	// to include this resource.
   488  	prefix := n.Original.Resource.Id()
   489  
   490  	// If we're present in the diff proper, then keep it. We're looking
   491  	// only for resources in the diff that match our resource or a count-index
   492  	// of our resource that are marked for destroy.
   493  	if d != nil {
   494  		for k, v := range d.Resources {
   495  			match := k == prefix || strings.HasPrefix(k, prefix+".")
   496  			if match && v.GetDestroy() {
   497  				return true
   498  			}
   499  		}
   500  	}
   501  
   502  	// If we're in the state as a primary in any form, then keep it.
   503  	// This does a prefix check so it will also catch orphans on count
   504  	// decreases to "1".
   505  	if s != nil {
   506  		for k, v := range s.Resources {
   507  			// Ignore exact matches
   508  			if k == prefix {
   509  				continue
   510  			}
   511  
   512  			// Ignore anything that doesn't have a "." afterwards so that
   513  			// we only get our own resource and any counts on it.
   514  			if !strings.HasPrefix(k, prefix+".") {
   515  				continue
   516  			}
   517  
   518  			// Ignore exact matches and the 0'th index. We only care
   519  			// about if there is a decrease in count.
   520  			if k == prefix+".0" {
   521  				continue
   522  			}
   523  
   524  			if v.Primary != nil {
   525  				return true
   526  			}
   527  		}
   528  
   529  		// If we're in the state as _both_ "foo" and "foo.0", then
   530  		// keep it, since we treat the latter as an orphan.
   531  		_, okOne := s.Resources[prefix]
   532  		_, okTwo := s.Resources[prefix+".0"]
   533  		if okOne && okTwo {
   534  			return true
   535  		}
   536  	}
   537  
   538  	return false
   539  }