github.com/blacked/terraform@v0.6.2-0.20150806163846-669c4ad71586/terraform/graph_builder.go (about)

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/hashicorp/terraform/config/module"
     7  )
     8  
     9  // GraphBuilder is an interface that can be implemented and used with
    10  // Terraform to build the graph that Terraform walks.
    11  type GraphBuilder interface {
    12  	// Build builds the graph for the given module path. It is up to
    13  	// the interface implementation whether this build should expand
    14  	// the graph or not.
    15  	Build(path []string) (*Graph, error)
    16  }
    17  
    18  // BasicGraphBuilder is a GraphBuilder that builds a graph out of a
    19  // series of transforms and (optionally) validates the graph is a valid
    20  // structure.
    21  type BasicGraphBuilder struct {
    22  	Steps    []GraphTransformer
    23  	Validate bool
    24  }
    25  
    26  func (b *BasicGraphBuilder) Build(path []string) (*Graph, error) {
    27  	g := &Graph{Path: path}
    28  	for _, step := range b.Steps {
    29  		if err := step.Transform(g); err != nil {
    30  			return g, err
    31  		}
    32  
    33  		log.Printf(
    34  			"[TRACE] Graph after step %T:\n\n%s",
    35  			step, g.String())
    36  	}
    37  
    38  	// Validate the graph structure
    39  	if b.Validate {
    40  		if err := g.Validate(); err != nil {
    41  			log.Printf("[ERROR] Graph validation failed. Graph:\n\n%s", g.String())
    42  			return nil, err
    43  		}
    44  	}
    45  
    46  	return g, nil
    47  }
    48  
    49  // BuiltinGraphBuilder is responsible for building the complete graph that
    50  // Terraform uses for execution. It is an opinionated builder that defines
    51  // the step order required to build a complete graph as is used and expected
    52  // by Terraform.
    53  //
    54  // If you require a custom graph, you'll have to build it up manually
    55  // on your own by building a new GraphBuilder implementation.
    56  type BuiltinGraphBuilder struct {
    57  	// Root is the root module of the graph to build.
    58  	Root *module.Tree
    59  
    60  	// Diff is the diff. The proper module diffs will be looked up.
    61  	Diff *Diff
    62  
    63  	// State is the global state. The proper module states will be looked
    64  	// up by graph path.
    65  	State *State
    66  
    67  	// Providers is the list of providers supported.
    68  	Providers []string
    69  
    70  	// Provisioners is the list of provisioners supported.
    71  	Provisioners []string
    72  
    73  	// Targets is the user-specified list of resources to target.
    74  	Targets []string
    75  
    76  	// Destroy is set to true when we're in a `terraform destroy` or a
    77  	// `terraform plan -destroy`
    78  	Destroy bool
    79  
    80  	// Determines whether the GraphBuilder should perform graph validation before
    81  	// returning the Graph. Generally you want this to be done, except when you'd
    82  	// like to inspect a problematic graph.
    83  	Validate bool
    84  
    85  	// Verbose is set to true when the graph should be built "worst case",
    86  	// skipping any prune steps. This is used for early cycle detection during
    87  	// Validate and for manual inspection via `terraform graph -verbose`.
    88  	Verbose bool
    89  }
    90  
    91  // Build builds the graph according to the steps returned by Steps.
    92  func (b *BuiltinGraphBuilder) Build(path []string) (*Graph, error) {
    93  	basic := &BasicGraphBuilder{
    94  		Steps:    b.Steps(path),
    95  		Validate: b.Validate,
    96  	}
    97  
    98  	return basic.Build(path)
    99  }
   100  
   101  // Steps returns the ordered list of GraphTransformers that must be executed
   102  // to build a complete graph.
   103  func (b *BuiltinGraphBuilder) Steps(path []string) []GraphTransformer {
   104  	steps := []GraphTransformer{
   105  		// Create all our resources from the configuration and state
   106  		&ConfigTransformer{Module: b.Root},
   107  		&OrphanTransformer{
   108  			State:     b.State,
   109  			Module:    b.Root,
   110  			Targeting: (len(b.Targets) > 0),
   111  		},
   112  
   113  		// Output-related transformations
   114  		&AddOutputOrphanTransformer{State: b.State},
   115  
   116  		// Provider-related transformations
   117  		&MissingProviderTransformer{Providers: b.Providers},
   118  		&ProviderTransformer{},
   119  		&DisableProviderTransformer{},
   120  
   121  		// Provisioner-related transformations
   122  		&MissingProvisionerTransformer{Provisioners: b.Provisioners},
   123  		&ProvisionerTransformer{},
   124  
   125  		// Run our vertex-level transforms
   126  		&VertexTransformer{
   127  			Transforms: []GraphVertexTransformer{
   128  				// Expand any statically expanded nodes, such as module graphs
   129  				&ExpandTransform{
   130  					Builder: b,
   131  				},
   132  			},
   133  		},
   134  
   135  		// Flatten stuff
   136  		&FlattenTransformer{},
   137  
   138  		// Make sure all the connections that are proxies are connected through
   139  		&ProxyTransformer{},
   140  
   141  		// Make sure we have a single root
   142  		&RootTransformer{},
   143  	}
   144  
   145  	// If we're on the root path, then we do a bunch of other stuff.
   146  	// We don't do the following for modules.
   147  	if len(path) <= 1 {
   148  		steps = append(steps,
   149  			// Optionally reduces the graph to a user-specified list of targets and
   150  			// their dependencies.
   151  			&TargetsTransformer{Targets: b.Targets, Destroy: b.Destroy},
   152  
   153  			// Prune the providers and provisioners. This must happen
   154  			// only once because flattened modules might depend on empty
   155  			// providers.
   156  			&PruneProviderTransformer{},
   157  			&PruneProvisionerTransformer{},
   158  
   159  			// Create the destruction nodes
   160  			&DestroyTransformer{FullDestroy: b.Destroy},
   161  			&CreateBeforeDestroyTransformer{},
   162  			b.conditional(&conditionalOpts{
   163  				If:   func() bool { return !b.Verbose },
   164  				Then: &PruneDestroyTransformer{Diff: b.Diff, State: b.State},
   165  			}),
   166  
   167  			// Remove the noop nodes
   168  			&PruneNoopTransformer{Diff: b.Diff, State: b.State},
   169  
   170  			// Insert nodes to close opened plugin connections
   171  			&CloseProviderTransformer{},
   172  			&CloseProvisionerTransformer{},
   173  
   174  			// Make sure we have a single root after the above changes.
   175  			// This is the 2nd root transformer. In practice this shouldn't
   176  			// actually matter as the RootTransformer is idempotent.
   177  			&RootTransformer{},
   178  
   179  			// Perform the transitive reduction to make our graph a bit
   180  			// more sane if possible (it usually is possible).
   181  			&TransitiveReductionTransformer{},
   182  		)
   183  	}
   184  
   185  	// Remove nils
   186  	for i, s := range steps {
   187  		if s == nil {
   188  			steps = append(steps[:i], steps[i+1:]...)
   189  		}
   190  	}
   191  
   192  	return steps
   193  }
   194  
   195  type conditionalOpts struct {
   196  	If   func() bool
   197  	Then GraphTransformer
   198  }
   199  
   200  func (b *BuiltinGraphBuilder) conditional(o *conditionalOpts) GraphTransformer {
   201  	if o.If != nil && o.Then != nil && o.If() {
   202  		return o.Then
   203  	}
   204  	return nil
   205  }