github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/terraform/transform_provisioner.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/go-multierror"
     7  	"github.com/hashicorp/terraform/dag"
     8  )
     9  
    10  // GraphNodeProvisioner is an interface that nodes that can be a provisioner
    11  // must implement. The ProvisionerName returned is the name of the provisioner
    12  // they satisfy.
    13  type GraphNodeProvisioner interface {
    14  	ProvisionerName() string
    15  }
    16  
    17  // GraphNodeCloseProvisioner is an interface that nodes that can be a close
    18  // provisioner must implement. The CloseProvisionerName returned is the name
    19  // of the provisioner they satisfy.
    20  type GraphNodeCloseProvisioner interface {
    21  	CloseProvisionerName() string
    22  }
    23  
    24  // GraphNodeProvisionerConsumer is an interface that nodes that require
    25  // a provisioner must implement. ProvisionedBy must return the name of the
    26  // provisioner to use.
    27  type GraphNodeProvisionerConsumer interface {
    28  	ProvisionedBy() []string
    29  }
    30  
    31  // ProvisionerTransformer is a GraphTransformer that maps resources to
    32  // provisioners within the graph. This will error if there are any resources
    33  // that don't map to proper resources.
    34  type ProvisionerTransformer struct{}
    35  
    36  func (t *ProvisionerTransformer) Transform(g *Graph) error {
    37  	// Go through the other nodes and match them to provisioners they need
    38  	var err error
    39  	m := provisionerVertexMap(g)
    40  	for _, v := range g.Vertices() {
    41  		if pv, ok := v.(GraphNodeProvisionerConsumer); ok {
    42  			for _, provisionerName := range pv.ProvisionedBy() {
    43  				target := m[provisionerName]
    44  				if target == nil {
    45  					err = multierror.Append(err, fmt.Errorf(
    46  						"%s: provisioner %s couldn't be found",
    47  						dag.VertexName(v), provisionerName))
    48  					continue
    49  				}
    50  
    51  				g.Connect(dag.BasicEdge(v, target))
    52  			}
    53  		}
    54  	}
    55  
    56  	return err
    57  }
    58  
    59  // CloseProvisionerTransformer is a GraphTransformer that adds nodes to the
    60  // graph that will close open provisioner connections that aren't needed
    61  // anymore. A provisioner connection is not needed anymore once all depended
    62  // resources in the graph are evaluated.
    63  type CloseProvisionerTransformer struct{}
    64  
    65  func (t *CloseProvisionerTransformer) Transform(g *Graph) error {
    66  	m := closeProvisionerVertexMap(g)
    67  	for _, v := range g.Vertices() {
    68  		if pv, ok := v.(GraphNodeProvisionerConsumer); ok {
    69  			for _, p := range pv.ProvisionedBy() {
    70  				source := m[p]
    71  
    72  				if source == nil {
    73  					// Create a new graphNodeCloseProvisioner and add it to the graph
    74  					source = &graphNodeCloseProvisioner{ProvisionerNameValue: p}
    75  					g.Add(source)
    76  
    77  					// Make sure we also add the new graphNodeCloseProvisioner to the map
    78  					// so we don't create and add any duplicate graphNodeCloseProvisioners.
    79  					m[p] = source
    80  				}
    81  
    82  				g.Connect(dag.BasicEdge(source, v))
    83  			}
    84  		}
    85  	}
    86  
    87  	return nil
    88  }
    89  
    90  // MissingProvisionerTransformer is a GraphTransformer that adds nodes
    91  // for missing provisioners into the graph. Specifically, it creates provisioner
    92  // configuration nodes for all the provisioners that we support. These are
    93  // pruned later during an optimization pass.
    94  type MissingProvisionerTransformer struct {
    95  	// Provisioners is the list of provisioners we support.
    96  	Provisioners []string
    97  }
    98  
    99  func (t *MissingProvisionerTransformer) Transform(g *Graph) error {
   100  	m := provisionerVertexMap(g)
   101  	for _, p := range t.Provisioners {
   102  		if _, ok := m[p]; ok {
   103  			// This provisioner already exists as a configured node
   104  			continue
   105  		}
   106  
   107  		// Add our own missing provisioner node to the graph
   108  		g.Add(&graphNodeMissingProvisioner{ProvisionerNameValue: p})
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  // PruneProvisionerTransformer is a GraphTransformer that prunes all the
   115  // provisioners that aren't needed from the graph. A provisioner is unneeded if
   116  // no resource or module is using that provisioner.
   117  type PruneProvisionerTransformer struct{}
   118  
   119  func (t *PruneProvisionerTransformer) Transform(g *Graph) error {
   120  	for _, v := range g.Vertices() {
   121  		// We only care about the provisioners
   122  		if _, ok := v.(GraphNodeProvisioner); !ok {
   123  			continue
   124  		}
   125  
   126  		// Does anything depend on this? If not, then prune it.
   127  		if s := g.UpEdges(v); s.Len() == 0 {
   128  			g.Remove(v)
   129  		}
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  func provisionerVertexMap(g *Graph) map[string]dag.Vertex {
   136  	m := make(map[string]dag.Vertex)
   137  	for _, v := range g.Vertices() {
   138  		if pv, ok := v.(GraphNodeProvisioner); ok {
   139  			m[pv.ProvisionerName()] = v
   140  		}
   141  	}
   142  
   143  	return m
   144  }
   145  
   146  func closeProvisionerVertexMap(g *Graph) map[string]dag.Vertex {
   147  	m := make(map[string]dag.Vertex)
   148  	for _, v := range g.Vertices() {
   149  		if pv, ok := v.(GraphNodeCloseProvisioner); ok {
   150  			m[pv.CloseProvisionerName()] = v
   151  		}
   152  	}
   153  
   154  	return m
   155  }
   156  
   157  type graphNodeCloseProvisioner struct {
   158  	ProvisionerNameValue string
   159  }
   160  
   161  func (n *graphNodeCloseProvisioner) Name() string {
   162  	return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue)
   163  }
   164  
   165  // GraphNodeEvalable impl.
   166  func (n *graphNodeCloseProvisioner) EvalTree() EvalNode {
   167  	return &EvalCloseProvisioner{Name: n.ProvisionerNameValue}
   168  }
   169  
   170  func (n *graphNodeCloseProvisioner) CloseProvisionerName() string {
   171  	return n.ProvisionerNameValue
   172  }
   173  
   174  type graphNodeMissingProvisioner struct {
   175  	ProvisionerNameValue string
   176  }
   177  
   178  func (n *graphNodeMissingProvisioner) Name() string {
   179  	return fmt.Sprintf("provisioner.%s", n.ProvisionerNameValue)
   180  }
   181  
   182  // GraphNodeEvalable impl.
   183  func (n *graphNodeMissingProvisioner) EvalTree() EvalNode {
   184  	return &EvalInitProvisioner{Name: n.ProvisionerNameValue}
   185  }
   186  
   187  func (n *graphNodeMissingProvisioner) ProvisionerName() string {
   188  	return n.ProvisionerNameValue
   189  }
   190  
   191  // GraphNodeFlattenable impl.
   192  func (n *graphNodeMissingProvisioner) Flatten(p []string) (dag.Vertex, error) {
   193  	return &graphNodeMissingProvisionerFlat{
   194  		graphNodeMissingProvisioner: n,
   195  		PathValue:                   p,
   196  	}, nil
   197  }
   198  
   199  // Same as graphNodeMissingProvisioner, but for flattening
   200  type graphNodeMissingProvisionerFlat struct {
   201  	*graphNodeMissingProvisioner
   202  
   203  	PathValue []string
   204  }
   205  
   206  func (n *graphNodeMissingProvisionerFlat) Name() string {
   207  	return fmt.Sprintf(
   208  		"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeMissingProvisioner.Name())
   209  }
   210  
   211  func (n *graphNodeMissingProvisionerFlat) Path() []string {
   212  	return n.PathValue
   213  }
   214  
   215  func (n *graphNodeMissingProvisionerFlat) ProvisionerName() string {
   216  	return fmt.Sprintf(
   217  		"%s.%s", modulePrefixStr(n.PathValue),
   218  		n.graphNodeMissingProvisioner.ProvisionerName())
   219  }