7 "github.com/hashicorp/terraform/config"
8 "github.com/hashicorp/terraform/dag"
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
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
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
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.
32 Config *config.Resource // Config is the resource in the config
33 ResourceState *ResourceState // ResourceState is the ResourceState for this
35 Targets []ResourceAddress // Set from GraphNodeTargetable
37 // The address of the provider this resource will use
38 ResolvedProvider string
41 func (n *NodeAbstractResource) Name() string {
42 return n.Addr.String()
46 func (n *NodeAbstractResource) Path() []string {
50 // GraphNodeReferenceable
51 func (n *NodeAbstractResource) ReferenceableName() []string {
52 // We always are referenceable as "type.name" as long as
53 // we have a config or address. Determine what that value is.
57 } else if n.Addr != nil {
58 addrCopy := n.Addr.Copy()
59 addrCopy.Path = nil // ReferenceTransformer handles paths
60 addrCopy.Index = -1 // We handle indexes below
61 id = addrCopy.String()
63 // No way to determine our type.name, just return
69 // Always include our own ID. This is primarily for backwards
70 // compatibility with states that didn't yet support the more
71 // specific dep string.
72 result = append(result, id)
74 // We represent all multi-access
75 result = append(result, fmt.Sprintf("%s.*", id))
77 // We represent either a specific number, or all numbers
85 suffix = fmt.Sprintf("%d", idx)
87 result = append(result, fmt.Sprintf("%s.%s", id, suffix))
92 // GraphNodeReferencer
93 func (n *NodeAbstractResource) References() []string {
94 // If we have a config, that is our source of truth
95 if c := n.Config; c != nil {
96 // Grab all the references
98 result = append(result, c.DependsOn...)
99 result = append(result, ReferencesFromConfig(c.RawCount)...)
100 result = append(result, ReferencesFromConfig(c.RawConfig)...)
101 for _, p := range c.Provisioners {
102 if p.When == config.ProvisionerWhenCreate {
103 result = append(result, ReferencesFromConfig(p.ConnInfo)...)
104 result = append(result, ReferencesFromConfig(p.RawConfig)...)
108 return uniqueStrings(result)
111 // If we have state, that is our next source
112 if s := n.ResourceState; s != nil {
113 return s.Dependencies
119 // StateReferences returns the dependencies to put into the state for
121 func (n *NodeAbstractResource) StateReferences() []string {
122 self := n.ReferenceableName()
124 // Determine what our "prefix" is for checking for references to
126 addrCopy := n.Addr.Copy()
128 selfPrefix := addrCopy.String() + "."
130 depsRaw := n.References()
131 deps := make([]string, 0, len(depsRaw))
132 for _, d := range depsRaw {
133 // Ignore any variable dependencies
134 if strings.HasPrefix(d, "var.") {
138 // If this has a backup ref, ignore those for now. The old state
139 // file never contained those and I'd rather store the rich types we
140 // add in the future.
141 if idx := strings.IndexRune(d, '/'); idx != -1 {
145 // If we're referencing ourself, then ignore it
147 for _, s := range self {
156 // If this is a reference to ourself and a specific index, we keep
157 // it. For example, if this resource is "foo.bar" and the reference
158 // is "foo.bar.0" then we keep it exact. Otherwise, we strip it.
159 if strings.HasSuffix(d, ".0") && !strings.HasPrefix(d, selfPrefix) {
163 // This is sad. The dependencies are currently in the format of
164 // "module.foo.bar" (the full field). This strips the field off.
165 if strings.HasPrefix(d, "module.") {
166 parts := strings.SplitN(d, ".", 3)
167 d = strings.Join(parts[0:2], ".")
170 deps = append(deps, d)
176 func (n *NodeAbstractResource) SetProvider(p string) {
177 n.ResolvedProvider = p
180 // GraphNodeProviderConsumer
181 func (n *NodeAbstractResource) ProvidedBy() string {
182 // If we have a config we prefer that above all else
184 return resourceProvider(n.Config.Type, n.Config.Provider)
187 // If we have state, then we will use the provider from there
188 if n.ResourceState != nil && n.ResourceState.Provider != "" {
189 return n.ResourceState.Provider
193 return resourceProvider(n.Addr.Type, "")
196 // GraphNodeProvisionerConsumer
197 func (n *NodeAbstractResource) ProvisionedBy() []string {
198 // If we have no configuration, then we have no provisioners
203 // Build the list of provisioners we need based on the configuration.
204 // It is okay to have duplicates here.
205 result := make([]string, len(n.Config.Provisioners))
206 for i, p := range n.Config.Provisioners {
213 // GraphNodeResource, GraphNodeAttachResourceState
214 func (n *NodeAbstractResource) ResourceAddr() *ResourceAddress {
218 // GraphNodeAddressable, TODO: remove, used by target, should unify
219 func (n *NodeAbstractResource) ResourceAddress() *ResourceAddress {
220 return n.ResourceAddr()
223 // GraphNodeTargetable
224 func (n *NodeAbstractResource) SetTargets(targets []ResourceAddress) {
228 // GraphNodeAttachResourceState
229 func (n *NodeAbstractResource) AttachResourceState(s *ResourceState) {
233 // GraphNodeAttachResourceConfig
234 func (n *NodeAbstractResource) AttachResourceConfig(c *config.Resource) {
238 // GraphNodeDotter impl.
239 func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
242 Attrs: map[string]string{