github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/terraform/node_resource_abstract.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/terraform/config"
     8  	"github.com/hashicorp/terraform/dag"
     9  )
    10  
    11  // ConcreteResourceNodeFunc is a callback type used to convert an
    12  // abstract resource to a concrete one of some type.
    13  type ConcreteResourceNodeFunc func(*NodeAbstractResource) dag.Vertex
    14  
    15  // GraphNodeResource is implemented by any nodes that represent a resource.
    16  // The type of operation cannot be assumed, only that this node represents
    17  // the given resource.
    18  type GraphNodeResource interface {
    19  	ResourceAddr() *ResourceAddress
    20  }
    21  
    22  // NodeAbstractResource represents a resource that has no associated
    23  // operations. It registers all the interfaces for a resource that common
    24  // across multiple operation types.
    25  type NodeAbstractResource struct {
    26  	Addr *ResourceAddress // Addr is the address for this resource
    27  
    28  	// The fields below will be automatically set using the Attach
    29  	// interfaces if you're running those transforms, but also be explicitly
    30  	// set if you already have that information.
    31  
    32  	Config        *config.Resource // Config is the resource in the config
    33  	ResourceState *ResourceState   // ResourceState is the ResourceState for this
    34  
    35  	Targets []ResourceAddress // Set from GraphNodeTargetable
    36  }
    37  
    38  func (n *NodeAbstractResource) Name() string {
    39  	return n.Addr.String()
    40  }
    41  
    42  // GraphNodeSubPath
    43  func (n *NodeAbstractResource) Path() []string {
    44  	return n.Addr.Path
    45  }
    46  
    47  // GraphNodeReferenceable
    48  func (n *NodeAbstractResource) ReferenceableName() []string {
    49  	// We always are referenceable as "type.name" as long as
    50  	// we have a config or address. Determine what that value is.
    51  	var id string
    52  	if n.Config != nil {
    53  		id = n.Config.Id()
    54  	} else if n.Addr != nil {
    55  		addrCopy := n.Addr.Copy()
    56  		addrCopy.Path = nil // ReferenceTransformer handles paths
    57  		addrCopy.Index = -1 // We handle indexes below
    58  		id = addrCopy.String()
    59  	} else {
    60  		// No way to determine our type.name, just return
    61  		return nil
    62  	}
    63  
    64  	var result []string
    65  
    66  	// Always include our own ID. This is primarily for backwards
    67  	// compatibility with states that didn't yet support the more
    68  	// specific dep string.
    69  	result = append(result, id)
    70  
    71  	// We represent all multi-access
    72  	result = append(result, fmt.Sprintf("%s.*", id))
    73  
    74  	// We represent either a specific number, or all numbers
    75  	suffix := "N"
    76  	if n.Addr != nil {
    77  		idx := n.Addr.Index
    78  		if idx == -1 {
    79  			idx = 0
    80  		}
    81  
    82  		suffix = fmt.Sprintf("%d", idx)
    83  	}
    84  	result = append(result, fmt.Sprintf("%s.%s", id, suffix))
    85  
    86  	return result
    87  }
    88  
    89  // GraphNodeReferencer
    90  func (n *NodeAbstractResource) References() []string {
    91  	// If we have a config, that is our source of truth
    92  	if c := n.Config; c != nil {
    93  		// Grab all the references
    94  		var result []string
    95  		result = append(result, c.DependsOn...)
    96  		result = append(result, ReferencesFromConfig(c.RawCount)...)
    97  		result = append(result, ReferencesFromConfig(c.RawConfig)...)
    98  		for _, p := range c.Provisioners {
    99  			if p.When == config.ProvisionerWhenCreate {
   100  				result = append(result, ReferencesFromConfig(p.ConnInfo)...)
   101  				result = append(result, ReferencesFromConfig(p.RawConfig)...)
   102  			}
   103  		}
   104  
   105  		return uniqueStrings(result)
   106  	}
   107  
   108  	// If we have state, that is our next source
   109  	if s := n.ResourceState; s != nil {
   110  		return s.Dependencies
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  // StateReferences returns the dependencies to put into the state for
   117  // this resource.
   118  func (n *NodeAbstractResource) StateReferences() []string {
   119  	self := n.ReferenceableName()
   120  
   121  	// Determine what our "prefix" is for checking for references to
   122  	// ourself.
   123  	addrCopy := n.Addr.Copy()
   124  	addrCopy.Index = -1
   125  	selfPrefix := addrCopy.String() + "."
   126  
   127  	depsRaw := n.References()
   128  	deps := make([]string, 0, len(depsRaw))
   129  	for _, d := range depsRaw {
   130  		// Ignore any variable dependencies
   131  		if strings.HasPrefix(d, "var.") {
   132  			continue
   133  		}
   134  
   135  		// If this has a backup ref, ignore those for now. The old state
   136  		// file never contained those and I'd rather store the rich types we
   137  		// add in the future.
   138  		if idx := strings.IndexRune(d, '/'); idx != -1 {
   139  			d = d[:idx]
   140  		}
   141  
   142  		// If we're referencing ourself, then ignore it
   143  		found := false
   144  		for _, s := range self {
   145  			if d == s {
   146  				found = true
   147  			}
   148  		}
   149  		if found {
   150  			continue
   151  		}
   152  
   153  		// If this is a reference to ourself and a specific index, we keep
   154  		// it. For example, if this resource is "foo.bar" and the reference
   155  		// is "foo.bar.0" then we keep it exact. Otherwise, we strip it.
   156  		if strings.HasSuffix(d, ".0") && !strings.HasPrefix(d, selfPrefix) {
   157  			d = d[:len(d)-2]
   158  		}
   159  
   160  		// This is sad. The dependencies are currently in the format of
   161  		// "module.foo.bar" (the full field). This strips the field off.
   162  		if strings.HasPrefix(d, "module.") {
   163  			parts := strings.SplitN(d, ".", 3)
   164  			d = strings.Join(parts[0:2], ".")
   165  		}
   166  
   167  		deps = append(deps, d)
   168  	}
   169  
   170  	return deps
   171  }
   172  
   173  // GraphNodeProviderConsumer
   174  func (n *NodeAbstractResource) ProvidedBy() []string {
   175  	// If we have a config we prefer that above all else
   176  	if n.Config != nil {
   177  		return []string{resourceProvider(n.Config.Type, n.Config.Provider)}
   178  	}
   179  
   180  	// If we have state, then we will use the provider from there
   181  	if n.ResourceState != nil && n.ResourceState.Provider != "" {
   182  		return []string{n.ResourceState.Provider}
   183  	}
   184  
   185  	// Use our type
   186  	return []string{resourceProvider(n.Addr.Type, "")}
   187  }
   188  
   189  // GraphNodeProvisionerConsumer
   190  func (n *NodeAbstractResource) ProvisionedBy() []string {
   191  	// If we have no configuration, then we have no provisioners
   192  	if n.Config == nil {
   193  		return nil
   194  	}
   195  
   196  	// Build the list of provisioners we need based on the configuration.
   197  	// It is okay to have duplicates here.
   198  	result := make([]string, len(n.Config.Provisioners))
   199  	for i, p := range n.Config.Provisioners {
   200  		result[i] = p.Type
   201  	}
   202  
   203  	return result
   204  }
   205  
   206  // GraphNodeResource, GraphNodeAttachResourceState
   207  func (n *NodeAbstractResource) ResourceAddr() *ResourceAddress {
   208  	return n.Addr
   209  }
   210  
   211  // GraphNodeAddressable, TODO: remove, used by target, should unify
   212  func (n *NodeAbstractResource) ResourceAddress() *ResourceAddress {
   213  	return n.ResourceAddr()
   214  }
   215  
   216  // GraphNodeTargetable
   217  func (n *NodeAbstractResource) SetTargets(targets []ResourceAddress) {
   218  	n.Targets = targets
   219  }
   220  
   221  // GraphNodeAttachResourceState
   222  func (n *NodeAbstractResource) AttachResourceState(s *ResourceState) {
   223  	n.ResourceState = s
   224  }
   225  
   226  // GraphNodeAttachResourceConfig
   227  func (n *NodeAbstractResource) AttachResourceConfig(c *config.Resource) {
   228  	n.Config = c
   229  }
   230  
   231  // GraphNodeDotter impl.
   232  func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
   233  	return &dag.DotNode{
   234  		Name: name,
   235  		Attrs: map[string]string{
   236  			"label": n.Name(),
   237  			"shape": "box",
   238  		},
   239  	}
   240  }