github.com/Hashicorp/terraform@v0.11.12-beta1/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 // The address of the provider this resource will use 38 ResolvedProvider string 39 } 40 41 func (n *NodeAbstractResource) Name() string { 42 return n.Addr.String() 43 } 44 45 // GraphNodeSubPath 46 func (n *NodeAbstractResource) Path() []string { 47 return n.Addr.Path 48 } 49 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. 54 var id string 55 if n.Config != nil { 56 id = n.Config.Id() 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() 62 } else { 63 // No way to determine our type.name, just return 64 return nil 65 } 66 67 var result []string 68 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) 73 74 // We represent all multi-access 75 result = append(result, fmt.Sprintf("%s.*", id)) 76 77 // We represent either a specific number, or all numbers 78 suffix := "N" 79 if n.Addr != nil { 80 idx := n.Addr.Index 81 if idx == -1 { 82 idx = 0 83 } 84 85 suffix = fmt.Sprintf("%d", idx) 86 } 87 result = append(result, fmt.Sprintf("%s.%s", id, suffix)) 88 89 return result 90 } 91 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 97 var result []string 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)...) 105 } 106 } 107 108 return uniqueStrings(result) 109 } 110 111 // If we have state, that is our next source 112 if s := n.ResourceState; s != nil { 113 return s.Dependencies 114 } 115 116 return nil 117 } 118 119 // StateReferences returns the dependencies to put into the state for 120 // this resource. 121 func (n *NodeAbstractResource) StateReferences() []string { 122 self := n.ReferenceableName() 123 124 // Determine what our "prefix" is for checking for references to 125 // ourself. 126 addrCopy := n.Addr.Copy() 127 addrCopy.Index = -1 128 selfPrefix := addrCopy.String() + "." 129 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.") { 135 continue 136 } 137 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 { 142 d = d[:idx] 143 } 144 145 // If we're referencing ourself, then ignore it 146 found := false 147 for _, s := range self { 148 if d == s { 149 found = true 150 } 151 } 152 if found { 153 continue 154 } 155 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) { 160 d = d[:len(d)-2] 161 } 162 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], ".") 168 } 169 170 deps = append(deps, d) 171 } 172 173 return deps 174 } 175 176 func (n *NodeAbstractResource) SetProvider(p string) { 177 n.ResolvedProvider = p 178 } 179 180 // GraphNodeProviderConsumer 181 func (n *NodeAbstractResource) ProvidedBy() string { 182 // If we have a config we prefer that above all else 183 if n.Config != nil { 184 return resourceProvider(n.Config.Type, n.Config.Provider) 185 } 186 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 190 } 191 192 // Use our type 193 return resourceProvider(n.Addr.Type, "") 194 } 195 196 // GraphNodeProvisionerConsumer 197 func (n *NodeAbstractResource) ProvisionedBy() []string { 198 // If we have no configuration, then we have no provisioners 199 if n.Config == nil { 200 return nil 201 } 202 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 { 207 result[i] = p.Type 208 } 209 210 return result 211 } 212 213 // GraphNodeResource, GraphNodeAttachResourceState 214 func (n *NodeAbstractResource) ResourceAddr() *ResourceAddress { 215 return n.Addr 216 } 217 218 // GraphNodeAddressable, TODO: remove, used by target, should unify 219 func (n *NodeAbstractResource) ResourceAddress() *ResourceAddress { 220 return n.ResourceAddr() 221 } 222 223 // GraphNodeTargetable 224 func (n *NodeAbstractResource) SetTargets(targets []ResourceAddress) { 225 n.Targets = targets 226 } 227 228 // GraphNodeAttachResourceState 229 func (n *NodeAbstractResource) AttachResourceState(s *ResourceState) { 230 n.ResourceState = s 231 } 232 233 // GraphNodeAttachResourceConfig 234 func (n *NodeAbstractResource) AttachResourceConfig(c *config.Resource) { 235 n.Config = c 236 } 237 238 // GraphNodeDotter impl. 239 func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { 240 return &dag.DotNode{ 241 Name: name, 242 Attrs: map[string]string{ 243 "label": n.Name(), 244 "shape": "box", 245 }, 246 } 247 }