github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/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 = &graphNodeProvisioner{ProvisionerNameValue: p}
   111  			if len(path) > 0 {
   112  				// If we have a path, we do the flattening immediately. This
   113  				// is to support new-style graph nodes that are already
   114  				// flattened.
   115  				if fn, ok := newV.(GraphNodeFlattenable); ok {
   116  					var err error
   117  					newV, err = fn.Flatten(path)
   118  					if err != nil {
   119  						return err
   120  					}
   121  				}
   122  			}
   123  
   124  			// Add the missing provisioner node to the graph
   125  			m[key] = g.Add(newV)
   126  		}
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  // CloseProvisionerTransformer is a GraphTransformer that adds nodes to the
   133  // graph that will close open provisioner connections that aren't needed
   134  // anymore. A provisioner connection is not needed anymore once all depended
   135  // resources in the graph are evaluated.
   136  type CloseProvisionerTransformer struct{}
   137  
   138  func (t *CloseProvisionerTransformer) Transform(g *Graph) error {
   139  	m := closeProvisionerVertexMap(g)
   140  	for _, v := range g.Vertices() {
   141  		if pv, ok := v.(GraphNodeProvisionerConsumer); ok {
   142  			for _, p := range pv.ProvisionedBy() {
   143  				source := m[p]
   144  
   145  				if source == nil {
   146  					// Create a new graphNodeCloseProvisioner and add it to the graph
   147  					source = &graphNodeCloseProvisioner{ProvisionerNameValue: p}
   148  					g.Add(source)
   149  
   150  					// Make sure we also add the new graphNodeCloseProvisioner to the map
   151  					// so we don't create and add any duplicate graphNodeCloseProvisioners.
   152  					m[p] = source
   153  				}
   154  
   155  				g.Connect(dag.BasicEdge(source, v))
   156  			}
   157  		}
   158  	}
   159  
   160  	return nil
   161  }
   162  
   163  // provisionerMapKey is a helper that gives us the key to use for the
   164  // maps returned by things such as provisionerVertexMap.
   165  func provisionerMapKey(k string, v dag.Vertex) string {
   166  	pathPrefix := ""
   167  	if sp, ok := v.(GraphNodeSubPath); ok {
   168  		raw := normalizeModulePath(sp.Path())
   169  		if len(raw) > len(rootModulePath) {
   170  			pathPrefix = modulePrefixStr(raw) + "."
   171  		}
   172  	}
   173  
   174  	return pathPrefix + k
   175  }
   176  
   177  func provisionerVertexMap(g *Graph) map[string]dag.Vertex {
   178  	m := make(map[string]dag.Vertex)
   179  	for _, v := range g.Vertices() {
   180  		if pv, ok := v.(GraphNodeProvisioner); ok {
   181  			m[pv.ProvisionerName()] = v
   182  		}
   183  	}
   184  
   185  	return m
   186  }
   187  
   188  func closeProvisionerVertexMap(g *Graph) map[string]dag.Vertex {
   189  	m := make(map[string]dag.Vertex)
   190  	for _, v := range g.Vertices() {
   191  		if pv, ok := v.(GraphNodeCloseProvisioner); ok {
   192  			m[pv.CloseProvisionerName()] = v
   193  		}
   194  	}
   195  
   196  	return m
   197  }
   198  
   199  type graphNodeCloseProvisioner struct {
   200  	ProvisionerNameValue string
   201  }
   202  
   203  func (n *graphNodeCloseProvisioner) Name() string {
   204  	return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue)
   205  }
   206  
   207  // GraphNodeEvalable impl.
   208  func (n *graphNodeCloseProvisioner) EvalTree() EvalNode {
   209  	return &EvalCloseProvisioner{Name: n.ProvisionerNameValue}
   210  }
   211  
   212  func (n *graphNodeCloseProvisioner) CloseProvisionerName() string {
   213  	return n.ProvisionerNameValue
   214  }
   215  
   216  type graphNodeProvisioner struct {
   217  	ProvisionerNameValue string
   218  }
   219  
   220  func (n *graphNodeProvisioner) Name() string {
   221  	return fmt.Sprintf("provisioner.%s", n.ProvisionerNameValue)
   222  }
   223  
   224  // GraphNodeEvalable impl.
   225  func (n *graphNodeProvisioner) EvalTree() EvalNode {
   226  	return &EvalInitProvisioner{Name: n.ProvisionerNameValue}
   227  }
   228  
   229  func (n *graphNodeProvisioner) ProvisionerName() string {
   230  	return n.ProvisionerNameValue
   231  }
   232  
   233  // GraphNodeFlattenable impl.
   234  func (n *graphNodeProvisioner) Flatten(p []string) (dag.Vertex, error) {
   235  	return &graphNodeProvisionerFlat{
   236  		graphNodeProvisioner: n,
   237  		PathValue:            p,
   238  	}, nil
   239  }
   240  
   241  // Same as graphNodeMissingProvisioner, but for flattening
   242  type graphNodeProvisionerFlat struct {
   243  	*graphNodeProvisioner
   244  
   245  	PathValue []string
   246  }
   247  
   248  func (n *graphNodeProvisionerFlat) Name() string {
   249  	return fmt.Sprintf(
   250  		"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeProvisioner.Name())
   251  }
   252  
   253  func (n *graphNodeProvisionerFlat) Path() []string {
   254  	return n.PathValue
   255  }
   256  
   257  func (n *graphNodeProvisionerFlat) ProvisionerName() string {
   258  	return fmt.Sprintf(
   259  		"%s.%s", modulePrefixStr(n.PathValue),
   260  		n.graphNodeProvisioner.ProvisionerName())
   261  }