]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / node_resource_abstract.go
CommitLineData
bae9f6d2
JC
1package terraform
2
3import (
4 "fmt"
107c1cdb
ND
5 "log"
6 "sort"
bae9f6d2 7
107c1cdb
ND
8 "github.com/hashicorp/terraform/addrs"
9 "github.com/hashicorp/terraform/configs"
10 "github.com/hashicorp/terraform/configs/configschema"
bae9f6d2 11 "github.com/hashicorp/terraform/dag"
107c1cdb
ND
12 "github.com/hashicorp/terraform/lang"
13 "github.com/hashicorp/terraform/states"
14 "github.com/hashicorp/terraform/tfdiags"
bae9f6d2
JC
15)
16
17// ConcreteResourceNodeFunc is a callback type used to convert an
18// abstract resource to a concrete one of some type.
19type ConcreteResourceNodeFunc func(*NodeAbstractResource) dag.Vertex
20
21// GraphNodeResource is implemented by any nodes that represent a resource.
22// The type of operation cannot be assumed, only that this node represents
23// the given resource.
24type GraphNodeResource interface {
107c1cdb
ND
25 ResourceAddr() addrs.AbsResource
26}
27
28// ConcreteResourceInstanceNodeFunc is a callback type used to convert an
29// abstract resource instance to a concrete one of some type.
30type ConcreteResourceInstanceNodeFunc func(*NodeAbstractResourceInstance) dag.Vertex
31
32// GraphNodeResourceInstance is implemented by any nodes that represent
33// a resource instance. A single resource may have multiple instances if,
34// for example, the "count" or "for_each" argument is used for it in
35// configuration.
36type GraphNodeResourceInstance interface {
37 ResourceInstanceAddr() addrs.AbsResourceInstance
bae9f6d2
JC
38}
39
40// NodeAbstractResource represents a resource that has no associated
41// operations. It registers all the interfaces for a resource that common
42// across multiple operation types.
43type NodeAbstractResource struct {
107c1cdb 44 Addr addrs.AbsResource // Addr is the address for this resource
bae9f6d2
JC
45
46 // The fields below will be automatically set using the Attach
47 // interfaces if you're running those transforms, but also be explicitly
48 // set if you already have that information.
49
107c1cdb
ND
50 Schema *configschema.Block // Schema for processing the configuration body
51 SchemaVersion uint64 // Schema version of "Schema", as decided by the provider
52 Config *configs.Resource // Config is the resource in the config
bae9f6d2 53
107c1cdb
ND
54 ProvisionerSchemas map[string]*configschema.Block
55
56 Targets []addrs.Targetable // Set from GraphNodeTargetable
15c0b25d
AP
57
58 // The address of the provider this resource will use
107c1cdb
ND
59 ResolvedProvider addrs.AbsProviderConfig
60}
61
62var (
63 _ GraphNodeSubPath = (*NodeAbstractResource)(nil)
64 _ GraphNodeReferenceable = (*NodeAbstractResource)(nil)
65 _ GraphNodeReferencer = (*NodeAbstractResource)(nil)
66 _ GraphNodeProviderConsumer = (*NodeAbstractResource)(nil)
67 _ GraphNodeProvisionerConsumer = (*NodeAbstractResource)(nil)
68 _ GraphNodeResource = (*NodeAbstractResource)(nil)
69 _ GraphNodeAttachResourceConfig = (*NodeAbstractResource)(nil)
70 _ GraphNodeAttachResourceSchema = (*NodeAbstractResource)(nil)
71 _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResource)(nil)
72 _ GraphNodeTargetable = (*NodeAbstractResource)(nil)
73 _ dag.GraphNodeDotter = (*NodeAbstractResource)(nil)
74)
75
76// NewNodeAbstractResource creates an abstract resource graph node for
77// the given absolute resource address.
78func NewNodeAbstractResource(addr addrs.AbsResource) *NodeAbstractResource {
79 return &NodeAbstractResource{
80 Addr: addr,
81 }
82}
83
84// NodeAbstractResourceInstance represents a resource instance with no
85// associated operations. It embeds NodeAbstractResource but additionally
86// contains an instance key, used to identify one of potentially many
87// instances that were created from a resource in configuration, e.g. using
88// the "count" or "for_each" arguments.
89type NodeAbstractResourceInstance struct {
90 NodeAbstractResource
91 InstanceKey addrs.InstanceKey
92
93 // The fields below will be automatically set using the Attach
94 // interfaces if you're running those transforms, but also be explicitly
95 // set if you already have that information.
96
97 ResourceState *states.Resource
98}
99
100var (
101 _ GraphNodeSubPath = (*NodeAbstractResourceInstance)(nil)
102 _ GraphNodeReferenceable = (*NodeAbstractResourceInstance)(nil)
103 _ GraphNodeReferencer = (*NodeAbstractResourceInstance)(nil)
104 _ GraphNodeProviderConsumer = (*NodeAbstractResourceInstance)(nil)
105 _ GraphNodeProvisionerConsumer = (*NodeAbstractResourceInstance)(nil)
106 _ GraphNodeResource = (*NodeAbstractResourceInstance)(nil)
107 _ GraphNodeResourceInstance = (*NodeAbstractResourceInstance)(nil)
108 _ GraphNodeAttachResourceState = (*NodeAbstractResourceInstance)(nil)
109 _ GraphNodeAttachResourceConfig = (*NodeAbstractResourceInstance)(nil)
110 _ GraphNodeAttachResourceSchema = (*NodeAbstractResourceInstance)(nil)
111 _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResourceInstance)(nil)
112 _ GraphNodeTargetable = (*NodeAbstractResourceInstance)(nil)
113 _ dag.GraphNodeDotter = (*NodeAbstractResourceInstance)(nil)
114)
115
116// NewNodeAbstractResourceInstance creates an abstract resource instance graph
117// node for the given absolute resource instance address.
118func NewNodeAbstractResourceInstance(addr addrs.AbsResourceInstance) *NodeAbstractResourceInstance {
119 // Due to the fact that we embed NodeAbstractResource, the given address
120 // actually ends up split between the resource address in the embedded
121 // object and the InstanceKey field in our own struct. The
122 // ResourceInstanceAddr method will stick these back together again on
123 // request.
124 return &NodeAbstractResourceInstance{
125 NodeAbstractResource: NodeAbstractResource{
126 Addr: addr.ContainingResource(),
127 },
128 InstanceKey: addr.Resource.Key,
129 }
bae9f6d2
JC
130}
131
132func (n *NodeAbstractResource) Name() string {
107c1cdb
ND
133 return n.ResourceAddr().String()
134}
135
136func (n *NodeAbstractResourceInstance) Name() string {
137 return n.ResourceInstanceAddr().String()
bae9f6d2
JC
138}
139
140// GraphNodeSubPath
107c1cdb
ND
141func (n *NodeAbstractResource) Path() addrs.ModuleInstance {
142 return n.Addr.Module
bae9f6d2
JC
143}
144
145// GraphNodeReferenceable
107c1cdb
ND
146func (n *NodeAbstractResource) ReferenceableAddrs() []addrs.Referenceable {
147 return []addrs.Referenceable{n.Addr.Resource}
148}
bae9f6d2 149
107c1cdb
ND
150// GraphNodeReferenceable
151func (n *NodeAbstractResourceInstance) ReferenceableAddrs() []addrs.Referenceable {
152 addr := n.ResourceInstanceAddr()
153 return []addrs.Referenceable{
154 addr.Resource,
155
156 // A resource instance can also be referenced by the address of its
157 // containing resource, so that e.g. a reference to aws_instance.foo
158 // would match both aws_instance.foo[0] and aws_instance.foo[1].
159 addr.ContainingResource().Resource,
160 }
161}
bae9f6d2 162
107c1cdb
ND
163// GraphNodeReferencer
164func (n *NodeAbstractResource) References() []*addrs.Reference {
165 // If we have a config then we prefer to use that.
166 if c := n.Config; c != nil {
167 var result []*addrs.Reference
168
169 for _, traversal := range c.DependsOn {
170 ref, err := addrs.ParseRef(traversal)
171 if err != nil {
172 // We ignore this here, because this isn't a suitable place to return
173 // errors. This situation should be caught and rejected during
174 // validation.
175 log.Printf("[ERROR] Can't parse %#v from depends_on as reference: %s", traversal, err)
176 continue
177 }
bae9f6d2 178
107c1cdb
ND
179 result = append(result, ref)
180 }
bae9f6d2 181
107c1cdb
ND
182 if n.Schema == nil {
183 // Should never happens, but we'll log if it does so that we can
184 // see this easily when debugging.
185 log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name())
bae9f6d2
JC
186 }
187
107c1cdb
ND
188 refs, _ := lang.ReferencesInExpr(c.Count)
189 result = append(result, refs...)
190 refs, _ = lang.ReferencesInBlock(c.Config, n.Schema)
191 result = append(result, refs...)
192 if c.Managed != nil {
193 for _, p := range c.Managed.Provisioners {
194 if p.When != configs.ProvisionerWhenCreate {
195 continue
196 }
197 if p.Connection != nil {
198 refs, _ = lang.ReferencesInBlock(p.Connection.Config, connectionBlockSupersetSchema)
199 result = append(result, refs...)
200 }
201
202 schema := n.ProvisionerSchemas[p.Type]
203 if schema == nil {
204 log.Printf("[WARN] no schema for provisioner %q is attached to %s, so provisioner block references cannot be detected", p.Type, n.Name())
205 }
206 refs, _ = lang.ReferencesInBlock(p.Config, schema)
207 result = append(result, refs...)
208 }
209 }
210 return result
bae9f6d2 211 }
bae9f6d2 212
107c1cdb
ND
213 // Otherwise, we have no references.
214 return nil
bae9f6d2
JC
215}
216
217// GraphNodeReferencer
107c1cdb
ND
218func (n *NodeAbstractResourceInstance) References() []*addrs.Reference {
219 // If we have a configuration attached then we'll delegate to our
220 // embedded abstract resource, which knows how to extract dependencies
221 // from configuration.
222 if n.Config != nil {
223 if n.Schema == nil {
224 // We'll produce a log message about this out here so that
225 // we can include the full instance address, since the equivalent
226 // message in NodeAbstractResource.References cannot see it.
227 log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name())
228 return nil
bae9f6d2 229 }
107c1cdb 230 return n.NodeAbstractResource.References()
bae9f6d2
JC
231 }
232
107c1cdb
ND
233 // Otherwise, if we have state then we'll use the values stored in state
234 // as a fallback.
235 if rs := n.ResourceState; rs != nil {
236 if s := rs.Instance(n.InstanceKey); s != nil {
237 // State is still storing dependencies as old-style strings, so we'll
238 // need to do a little work here to massage this to the form we now
239 // want.
240 var result []*addrs.Reference
241 for _, addr := range s.Current.Dependencies {
242 if addr == nil {
243 // Should never happen; indicates a bug in the state loader
244 panic(fmt.Sprintf("dependencies for current object on %s contains nil address", n.ResourceInstanceAddr()))
245 }
246
247 // This is a little weird: we need to manufacture an addrs.Reference
248 // with a fake range here because the state isn't something we can
249 // make source references into.
250 result = append(result, &addrs.Reference{
251 Subject: addr,
252 SourceRange: tfdiags.SourceRange{
253 Filename: "(state file)",
254 },
255 })
256 }
257 return result
258 }
bae9f6d2
JC
259 }
260
107c1cdb 261 // If we have neither config nor state then we have no references.
bae9f6d2
JC
262 return nil
263}
264
107c1cdb
ND
265// converts an instance address to the legacy dotted notation
266func dottedInstanceAddr(tr addrs.ResourceInstance) string {
267 // The legacy state format uses dot-separated instance keys,
268 // rather than bracketed as in our modern syntax.
269 var suffix string
270 switch tk := tr.Key.(type) {
271 case addrs.IntKey:
272 suffix = fmt.Sprintf(".%d", int(tk))
273 case addrs.StringKey:
274 suffix = fmt.Sprintf(".%s", string(tk))
275 }
276 return tr.Resource.String() + suffix
277}
278
bae9f6d2
JC
279// StateReferences returns the dependencies to put into the state for
280// this resource.
107c1cdb
ND
281func (n *NodeAbstractResourceInstance) StateReferences() []addrs.Referenceable {
282 selfAddrs := n.ReferenceableAddrs()
283
284 // Since we don't include the source location references in our
285 // results from this method, we'll also filter out duplicates:
286 // there's no point in listing the same object twice without
287 // that additional context.
288 seen := map[string]struct{}{}
289
290 // Pretend that we've already "seen" all of our own addresses so that we
291 // won't record self-references in the state. This can arise if, for
292 // example, a provisioner for a resource refers to the resource itself,
293 // which is valid (since provisioners always run after apply) but should
294 // not create an explicit dependency edge.
295 for _, selfAddr := range selfAddrs {
296 seen[selfAddr.String()] = struct{}{}
297 if riAddr, ok := selfAddr.(addrs.ResourceInstance); ok {
298 seen[riAddr.ContainingResource().String()] = struct{}{}
299 }
300 }
bae9f6d2
JC
301
302 depsRaw := n.References()
107c1cdb 303 deps := make([]addrs.Referenceable, 0, len(depsRaw))
bae9f6d2 304 for _, d := range depsRaw {
107c1cdb
ND
305 subj := d.Subject
306 if mco, isOutput := subj.(addrs.ModuleCallOutput); isOutput {
307 // For state dependencies, we simplify outputs to just refer
308 // to the module as a whole. It's not really clear why we do this,
309 // but this logic is preserved from before the 0.12 rewrite of
310 // this function.
311 subj = mco.Call
bae9f6d2
JC
312 }
313
107c1cdb
ND
314 k := subj.String()
315 if _, exists := seen[k]; exists {
bae9f6d2
JC
316 continue
317 }
107c1cdb
ND
318 seen[k] = struct{}{}
319 switch tr := subj.(type) {
320 case addrs.ResourceInstance:
321 deps = append(deps, tr)
322 case addrs.Resource:
323 deps = append(deps, tr)
324 case addrs.ModuleCallInstance:
325 deps = append(deps, tr)
326 default:
327 // No other reference types are recorded in the state.
bae9f6d2 328 }
bae9f6d2
JC
329 }
330
107c1cdb
ND
331 // We'll also sort them, since that'll avoid creating changes in the
332 // serialized state that make no semantic difference.
333 sort.Slice(deps, func(i, j int) bool {
334 // Simple string-based sort because we just care about consistency,
335 // not user-friendliness.
336 return deps[i].String() < deps[j].String()
337 })
338
bae9f6d2
JC
339 return deps
340}
341
107c1cdb 342func (n *NodeAbstractResource) SetProvider(p addrs.AbsProviderConfig) {
15c0b25d
AP
343 n.ResolvedProvider = p
344}
345
bae9f6d2 346// GraphNodeProviderConsumer
107c1cdb 347func (n *NodeAbstractResource) ProvidedBy() (addrs.AbsProviderConfig, bool) {
bae9f6d2
JC
348 // If we have a config we prefer that above all else
349 if n.Config != nil {
107c1cdb
ND
350 relAddr := n.Config.ProviderConfigAddr()
351 return relAddr.Absolute(n.Path()), false
352 }
353
354 // Use our type and containing module path to guess a provider configuration address
355 return n.Addr.Resource.DefaultProviderConfig().Absolute(n.Addr.Module), false
356}
357
358// GraphNodeProviderConsumer
359func (n *NodeAbstractResourceInstance) ProvidedBy() (addrs.AbsProviderConfig, bool) {
360 // If we have a config we prefer that above all else
361 if n.Config != nil {
362 relAddr := n.Config.ProviderConfigAddr()
363 return relAddr.Absolute(n.Path()), false
bae9f6d2
JC
364 }
365
366 // If we have state, then we will use the provider from there
107c1cdb
ND
367 if n.ResourceState != nil {
368 // An address from the state must match exactly, since we must ensure
369 // we refresh/destroy a resource with the same provider configuration
370 // that created it.
371 return n.ResourceState.ProviderConfig, true
bae9f6d2
JC
372 }
373
107c1cdb
ND
374 // Use our type and containing module path to guess a provider configuration address
375 return n.Addr.Resource.DefaultProviderConfig().Absolute(n.Path()), false
bae9f6d2
JC
376}
377
378// GraphNodeProvisionerConsumer
379func (n *NodeAbstractResource) ProvisionedBy() []string {
380 // If we have no configuration, then we have no provisioners
107c1cdb 381 if n.Config == nil || n.Config.Managed == nil {
bae9f6d2
JC
382 return nil
383 }
384
385 // Build the list of provisioners we need based on the configuration.
386 // It is okay to have duplicates here.
107c1cdb
ND
387 result := make([]string, len(n.Config.Managed.Provisioners))
388 for i, p := range n.Config.Managed.Provisioners {
bae9f6d2
JC
389 result[i] = p.Type
390 }
391
392 return result
393}
394
107c1cdb
ND
395// GraphNodeProvisionerConsumer
396func (n *NodeAbstractResource) AttachProvisionerSchema(name string, schema *configschema.Block) {
397 if n.ProvisionerSchemas == nil {
398 n.ProvisionerSchemas = make(map[string]*configschema.Block)
399 }
400 n.ProvisionerSchemas[name] = schema
401}
402
403// GraphNodeResource
404func (n *NodeAbstractResource) ResourceAddr() addrs.AbsResource {
bae9f6d2
JC
405 return n.Addr
406}
407
107c1cdb
ND
408// GraphNodeResourceInstance
409func (n *NodeAbstractResourceInstance) ResourceInstanceAddr() addrs.AbsResourceInstance {
410 return n.NodeAbstractResource.Addr.Instance(n.InstanceKey)
411}
412
bae9f6d2
JC
413// GraphNodeAddressable, TODO: remove, used by target, should unify
414func (n *NodeAbstractResource) ResourceAddress() *ResourceAddress {
107c1cdb 415 return NewLegacyResourceAddress(n.Addr)
bae9f6d2
JC
416}
417
418// GraphNodeTargetable
107c1cdb 419func (n *NodeAbstractResource) SetTargets(targets []addrs.Targetable) {
bae9f6d2
JC
420 n.Targets = targets
421}
422
423// GraphNodeAttachResourceState
107c1cdb 424func (n *NodeAbstractResourceInstance) AttachResourceState(s *states.Resource) {
bae9f6d2
JC
425 n.ResourceState = s
426}
427
428// GraphNodeAttachResourceConfig
107c1cdb 429func (n *NodeAbstractResource) AttachResourceConfig(c *configs.Resource) {
bae9f6d2
JC
430 n.Config = c
431}
432
107c1cdb
ND
433// GraphNodeAttachResourceSchema impl
434func (n *NodeAbstractResource) AttachResourceSchema(schema *configschema.Block, version uint64) {
435 n.Schema = schema
436 n.SchemaVersion = version
437}
438
bae9f6d2
JC
439// GraphNodeDotter impl.
440func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
441 return &dag.DotNode{
442 Name: name,
443 Attrs: map[string]string{
444 "label": n.Name(),
445 "shape": "box",
446 },
447 }
448}