terraform/terraform/node_resource_abstract.go

238 lines
6.3 KiB
Go

package terraform
import (
"fmt"
"strings"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/dag"
)
// ConcreteResourceNodeFunc is a callback type used to convert an
// abstract resource to a concrete one of some type.
type ConcreteResourceNodeFunc func(*NodeAbstractResource) dag.Vertex
// GraphNodeResource is implemented by any nodes that represent a resource.
// The type of operation cannot be assumed, only that this node represents
// the given resource.
type GraphNodeResource interface {
ResourceAddr() *ResourceAddress
}
// NodeAbstractResource represents a resource that has no associated
// operations. It registers all the interfaces for a resource that common
// across multiple operation types.
type NodeAbstractResource struct {
Addr *ResourceAddress // Addr is the address for this resource
// The fields below will be automatically set using the Attach
// interfaces if you're running those transforms, but also be explicitly
// set if you already have that information.
Config *config.Resource // Config is the resource in the config
ResourceState *ResourceState // ResourceState is the ResourceState for this
Targets []ResourceAddress // Set from GraphNodeTargetable
}
func (n *NodeAbstractResource) Name() string {
return n.Addr.String()
}
// GraphNodeSubPath
func (n *NodeAbstractResource) Path() []string {
return n.Addr.Path
}
// GraphNodeReferenceable
func (n *NodeAbstractResource) ReferenceableName() []string {
// We always are referenceable as "type.name" as long as
// we have a config or address. Determine what that value is.
var id string
if n.Config != nil {
id = n.Config.Id()
} else if n.Addr != nil {
addrCopy := n.Addr.Copy()
addrCopy.Index = -1
id = addrCopy.String()
} else {
// No way to determine our type.name, just return
return nil
}
var result []string
// Always include our own ID. This is primarily for backwards
// compatibility with states that didn't yet support the more
// specific dep string.
result = append(result, id)
// We represent all multi-access
result = append(result, fmt.Sprintf("%s.*", id))
// We represent either a specific number, or all numbers
suffix := "N"
if n.Addr != nil {
idx := n.Addr.Index
if idx == -1 {
idx = 0
}
suffix = fmt.Sprintf("%d", idx)
}
result = append(result, fmt.Sprintf("%s.%s", id, suffix))
return result
}
// GraphNodeReferencer
func (n *NodeAbstractResource) References() []string {
// If we have a config, that is our source of truth
if c := n.Config; c != nil {
// Grab all the references
var result []string
result = append(result, c.DependsOn...)
result = append(result, ReferencesFromConfig(c.RawCount)...)
result = append(result, ReferencesFromConfig(c.RawConfig)...)
for _, p := range c.Provisioners {
result = append(result, ReferencesFromConfig(p.ConnInfo)...)
result = append(result, ReferencesFromConfig(p.RawConfig)...)
}
return result
}
// If we have state, that is our next source
if s := n.ResourceState; s != nil {
return s.Dependencies
}
return nil
}
// StateReferences returns the dependencies to put into the state for
// this resource.
func (n *NodeAbstractResource) StateReferences() []string {
self := n.ReferenceableName()
// Determine what our "prefix" is for checking for references to
// ourself.
addrCopy := n.Addr.Copy()
addrCopy.Index = -1
selfPrefix := addrCopy.String() + "."
depsRaw := n.References()
deps := make([]string, 0, len(depsRaw))
for _, d := range depsRaw {
// Ignore any variable dependencies
if strings.HasPrefix(d, "var.") {
continue
}
// If this has a backup ref, ignore those for now. The old state
// file never contained those and I'd rather store the rich types we
// add in the future.
if idx := strings.IndexRune(d, '/'); idx != -1 {
d = d[:idx]
}
// If we're referencing ourself, then ignore it
found := false
for _, s := range self {
if d == s {
found = true
}
}
if found {
continue
}
// If this is a reference to ourself and a specific index, we keep
// it. For example, if this resource is "foo.bar" and the reference
// is "foo.bar.0" then we keep it exact. Otherwise, we strip it.
if strings.HasSuffix(d, ".0") && !strings.HasPrefix(d, selfPrefix) {
d = d[:len(d)-2]
}
// This is sad. The dependencies are currently in the format of
// "module.foo.bar" (the full field). This strips the field off.
if strings.HasPrefix(d, "module.") {
parts := strings.SplitN(d, ".", 3)
d = strings.Join(parts[0:2], ".")
}
deps = append(deps, d)
}
return deps
}
// GraphNodeProviderConsumer
func (n *NodeAbstractResource) ProvidedBy() []string {
// If we have a config we prefer that above all else
if n.Config != nil {
return []string{resourceProvider(n.Config.Type, n.Config.Provider)}
}
// If we have state, then we will use the provider from there
if n.ResourceState != nil && n.ResourceState.Provider != "" {
return []string{n.ResourceState.Provider}
}
// Use our type
return []string{resourceProvider(n.Addr.Type, "")}
}
// GraphNodeProvisionerConsumer
func (n *NodeAbstractResource) ProvisionedBy() []string {
// If we have no configuration, then we have no provisioners
if n.Config == nil {
return nil
}
// Build the list of provisioners we need based on the configuration.
// It is okay to have duplicates here.
result := make([]string, len(n.Config.Provisioners))
for i, p := range n.Config.Provisioners {
result[i] = p.Type
}
return result
}
// GraphNodeResource, GraphNodeAttachResourceState
func (n *NodeAbstractResource) ResourceAddr() *ResourceAddress {
return n.Addr
}
// GraphNodeAddressable, TODO: remove, used by target, should unify
func (n *NodeAbstractResource) ResourceAddress() *ResourceAddress {
return n.ResourceAddr()
}
// GraphNodeTargetable
func (n *NodeAbstractResource) SetTargets(targets []ResourceAddress) {
n.Targets = targets
}
// GraphNodeAttachResourceState
func (n *NodeAbstractResource) AttachResourceState(s *ResourceState) {
n.ResourceState = s
}
// GraphNodeAttachResourceConfig
func (n *NodeAbstractResource) AttachResourceConfig(c *config.Resource) {
n.Config = c
}
// GraphNodeDotter impl.
func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": n.Name(),
"shape": "box",
},
}
}