github.com/tarrant/terraform@v0.3.8-0.20150402012457-f68c9eee638e/terraform/graph_config_node.go (about)

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