]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - terraform/node_resource_abstract.go
Merge branch 'fix_read_test' of github.com:alexandreFre/terraform-provider-statuscake
[github/fretlink/terraform-provider-statuscake.git] / terraform / node_resource_abstract.go
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 }