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