github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/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 _, p := range pv.ProvisionedBy() {
    43  				if m[p] == nil {
    44  					err = multierror.Append(err, fmt.Errorf(
    45  						"%s: provisioner %s couldn't be found",
    46  						dag.VertexName(v), p))
    47  					continue
    48  				}
    49  
    50  				g.Connect(dag.BasicEdge(v, m[p]))
    51  			}
    52  		}
    53  	}
    54  
    55  	return err
    56  }
    57  
    58  // MissingProvisionerTransformer is a GraphTransformer that adds nodes
    59  // for missing provisioners into the graph.
    60  type MissingProvisionerTransformer struct {
    61  	// Provisioners is the list of provisioners we support.
    62  	Provisioners []string
    63  }
    64  
    65  func (t *MissingProvisionerTransformer) Transform(g *Graph) error {
    66  	// Create a set of our supported provisioners
    67  	supported := make(map[string]struct{}, len(t.Provisioners))
    68  	for _, v := range t.Provisioners {
    69  		supported[v] = struct{}{}
    70  	}
    71  
    72  	// Get the map of provisioners we already have in our graph
    73  	m := provisionerVertexMap(g)
    74  
    75  	// Go through all the provisioner consumers and make sure we add
    76  	// that provisioner if it is missing.
    77  	for _, v := range g.Vertices() {
    78  		pv, ok := v.(GraphNodeProvisionerConsumer)
    79  		if !ok {
    80  			continue
    81  		}
    82  
    83  		for _, p := range pv.ProvisionedBy() {
    84  			if _, ok := m[p]; ok {
    85  				// This provisioner already exists as a configure node
    86  				continue
    87  			}
    88  
    89  			if _, ok := supported[p]; !ok {
    90  				// If we don't support the provisioner type, skip it.
    91  				// Validation later will catch this as an error.
    92  				continue
    93  			}
    94  
    95  			// Add the missing provisioner node to the graph
    96  			m[p] = g.Add(&graphNodeProvisioner{ProvisionerNameValue: p})
    97  		}
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  // CloseProvisionerTransformer is a GraphTransformer that adds nodes to the
   104  // graph that will close open provisioner connections that aren't needed
   105  // anymore. A provisioner connection is not needed anymore once all depended
   106  // resources in the graph are evaluated.
   107  type CloseProvisionerTransformer struct{}
   108  
   109  func (t *CloseProvisionerTransformer) Transform(g *Graph) error {
   110  	m := closeProvisionerVertexMap(g)
   111  	for _, v := range g.Vertices() {
   112  		if pv, ok := v.(GraphNodeProvisionerConsumer); ok {
   113  			for _, p := range pv.ProvisionedBy() {
   114  				source := m[p]
   115  
   116  				if source == nil {
   117  					// Create a new graphNodeCloseProvisioner and add it to the graph
   118  					source = &graphNodeCloseProvisioner{ProvisionerNameValue: p}
   119  					g.Add(source)
   120  
   121  					// Make sure we also add the new graphNodeCloseProvisioner to the map
   122  					// so we don't create and add any duplicate graphNodeCloseProvisioners.
   123  					m[p] = source
   124  				}
   125  
   126  				g.Connect(dag.BasicEdge(source, v))
   127  			}
   128  		}
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  func provisionerVertexMap(g *Graph) map[string]dag.Vertex {
   135  	m := make(map[string]dag.Vertex)
   136  	for _, v := range g.Vertices() {
   137  		if pv, ok := v.(GraphNodeProvisioner); ok {
   138  			m[pv.ProvisionerName()] = v
   139  		}
   140  	}
   141  
   142  	return m
   143  }
   144  
   145  func closeProvisionerVertexMap(g *Graph) map[string]dag.Vertex {
   146  	m := make(map[string]dag.Vertex)
   147  	for _, v := range g.Vertices() {
   148  		if pv, ok := v.(GraphNodeCloseProvisioner); ok {
   149  			m[pv.CloseProvisionerName()] = v
   150  		}
   151  	}
   152  
   153  	return m
   154  }
   155  
   156  type graphNodeCloseProvisioner struct {
   157  	ProvisionerNameValue string
   158  }
   159  
   160  func (n *graphNodeCloseProvisioner) Name() string {
   161  	return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue)
   162  }
   163  
   164  // GraphNodeEvalable impl.
   165  func (n *graphNodeCloseProvisioner) EvalTree() EvalNode {
   166  	return &EvalCloseProvisioner{Name: n.ProvisionerNameValue}
   167  }
   168  
   169  func (n *graphNodeCloseProvisioner) CloseProvisionerName() string {
   170  	return n.ProvisionerNameValue
   171  }
   172  
   173  type graphNodeProvisioner struct {
   174  	ProvisionerNameValue string
   175  }
   176  
   177  func (n *graphNodeProvisioner) Name() string {
   178  	return fmt.Sprintf("provisioner.%s", n.ProvisionerNameValue)
   179  }
   180  
   181  // GraphNodeEvalable impl.
   182  func (n *graphNodeProvisioner) EvalTree() EvalNode {
   183  	return &EvalInitProvisioner{Name: n.ProvisionerNameValue}
   184  }
   185  
   186  func (n *graphNodeProvisioner) ProvisionerName() string {
   187  	return n.ProvisionerNameValue
   188  }
   189  
   190  // GraphNodeFlattenable impl.
   191  func (n *graphNodeProvisioner) Flatten(p []string) (dag.Vertex, error) {
   192  	return &graphNodeProvisionerFlat{
   193  		graphNodeProvisioner: n,
   194  		PathValue:            p,
   195  	}, nil
   196  }
   197  
   198  // Same as graphNodeMissingProvisioner, but for flattening
   199  type graphNodeProvisionerFlat struct {
   200  	*graphNodeProvisioner
   201  
   202  	PathValue []string
   203  }
   204  
   205  func (n *graphNodeProvisionerFlat) Name() string {
   206  	return fmt.Sprintf(
   207  		"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeProvisioner.Name())
   208  }
   209  
   210  func (n *graphNodeProvisionerFlat) Path() []string {
   211  	return n.PathValue
   212  }
   213  
   214  func (n *graphNodeProvisionerFlat) ProvisionerName() string {
   215  	return fmt.Sprintf(
   216  		"%s.%s", modulePrefixStr(n.PathValue),
   217  		n.graphNodeProvisioner.ProvisionerName())
   218  }