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