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