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 }