]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "fmt" | |
bae9f6d2 | 5 | |
107c1cdb ND |
6 | "github.com/hashicorp/terraform/addrs" |
7 | "github.com/hashicorp/terraform/configs" | |
9b12e4fe | 8 | "github.com/hashicorp/terraform/dag" |
107c1cdb | 9 | "github.com/hashicorp/terraform/lang" |
bae9f6d2 JC |
10 | ) |
11 | ||
12 | // NodeApplyableOutput represents an output that is "applyable": | |
13 | // it is ready to be applied. | |
14 | type NodeApplyableOutput struct { | |
107c1cdb ND |
15 | Addr addrs.AbsOutputValue |
16 | Config *configs.Output // Config is the output in the config | |
bae9f6d2 JC |
17 | } |
18 | ||
107c1cdb ND |
19 | var ( |
20 | _ GraphNodeSubPath = (*NodeApplyableOutput)(nil) | |
21 | _ RemovableIfNotTargeted = (*NodeApplyableOutput)(nil) | |
22 | _ GraphNodeTargetDownstream = (*NodeApplyableOutput)(nil) | |
23 | _ GraphNodeReferenceable = (*NodeApplyableOutput)(nil) | |
24 | _ GraphNodeReferencer = (*NodeApplyableOutput)(nil) | |
25 | _ GraphNodeReferenceOutside = (*NodeApplyableOutput)(nil) | |
26 | _ GraphNodeEvalable = (*NodeApplyableOutput)(nil) | |
27 | _ dag.GraphNodeDotter = (*NodeApplyableOutput)(nil) | |
28 | ) | |
bae9f6d2 | 29 | |
107c1cdb ND |
30 | func (n *NodeApplyableOutput) Name() string { |
31 | return n.Addr.String() | |
bae9f6d2 JC |
32 | } |
33 | ||
34 | // GraphNodeSubPath | |
107c1cdb ND |
35 | func (n *NodeApplyableOutput) Path() addrs.ModuleInstance { |
36 | return n.Addr.Module | |
bae9f6d2 JC |
37 | } |
38 | ||
39 | // RemovableIfNotTargeted | |
40 | func (n *NodeApplyableOutput) RemoveIfNotTargeted() bool { | |
41 | // We need to add this so that this node will be removed if | |
42 | // it isn't targeted or a dependency of a target. | |
43 | return true | |
44 | } | |
45 | ||
9b12e4fe JC |
46 | // GraphNodeTargetDownstream |
47 | func (n *NodeApplyableOutput) TargetDownstream(targetedDeps, untargetedDeps *dag.Set) bool { | |
48 | // If any of the direct dependencies of an output are targeted then | |
49 | // the output must always be targeted as well, so its value will always | |
50 | // be up-to-date at the completion of an apply walk. | |
51 | return true | |
52 | } | |
53 | ||
107c1cdb ND |
54 | func referenceOutsideForOutput(addr addrs.AbsOutputValue) (selfPath, referencePath addrs.ModuleInstance) { |
55 | ||
56 | // Output values have their expressions resolved in the context of the | |
57 | // module where they are defined. | |
58 | referencePath = addr.Module | |
59 | ||
60 | // ...but they are referenced in the context of their calling module. | |
61 | selfPath = addr.Module.Parent() | |
62 | ||
63 | return // uses named return values | |
64 | ||
65 | } | |
66 | ||
67 | // GraphNodeReferenceOutside implementation | |
68 | func (n *NodeApplyableOutput) ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance) { | |
69 | return referenceOutsideForOutput(n.Addr) | |
70 | } | |
71 | ||
72 | func referenceableAddrsForOutput(addr addrs.AbsOutputValue) []addrs.Referenceable { | |
73 | // An output in the root module can't be referenced at all. | |
74 | if addr.Module.IsRoot() { | |
75 | return nil | |
76 | } | |
77 | ||
78 | // Otherwise, we can be referenced via a reference to our output name | |
79 | // on the parent module's call, or via a reference to the entire call. | |
80 | // e.g. module.foo.bar or just module.foo . | |
81 | // Note that our ReferenceOutside method causes these addresses to be | |
82 | // relative to the calling module, not the module where the output | |
83 | // was declared. | |
84 | _, outp := addr.ModuleCallOutput() | |
85 | _, call := addr.Module.CallInstance() | |
86 | return []addrs.Referenceable{outp, call} | |
87 | ||
88 | } | |
89 | ||
bae9f6d2 | 90 | // GraphNodeReferenceable |
107c1cdb ND |
91 | func (n *NodeApplyableOutput) ReferenceableAddrs() []addrs.Referenceable { |
92 | return referenceableAddrsForOutput(n.Addr) | |
bae9f6d2 JC |
93 | } |
94 | ||
107c1cdb ND |
95 | func referencesForOutput(c *configs.Output) []*addrs.Reference { |
96 | impRefs, _ := lang.ReferencesInExpr(c.Expr) | |
97 | expRefs, _ := lang.References(c.DependsOn) | |
98 | l := len(impRefs) + len(expRefs) | |
99 | if l == 0 { | |
100 | return nil | |
bae9f6d2 | 101 | } |
107c1cdb ND |
102 | refs := make([]*addrs.Reference, 0, l) |
103 | refs = append(refs, impRefs...) | |
104 | refs = append(refs, expRefs...) | |
105 | return refs | |
bae9f6d2 | 106 | |
107c1cdb ND |
107 | } |
108 | ||
109 | // GraphNodeReferencer | |
110 | func (n *NodeApplyableOutput) References() []*addrs.Reference { | |
111 | return appendResourceDestroyReferences(referencesForOutput(n.Config)) | |
bae9f6d2 JC |
112 | } |
113 | ||
114 | // GraphNodeEvalable | |
115 | func (n *NodeApplyableOutput) EvalTree() EvalNode { | |
15c0b25d AP |
116 | return &EvalSequence{ |
117 | Nodes: []EvalNode{ | |
15c0b25d AP |
118 | &EvalOpFilter{ |
119 | Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy}, | |
120 | Node: &EvalWriteOutput{ | |
107c1cdb | 121 | Addr: n.Addr.OutputValue, |
bae9f6d2 | 122 | Sensitive: n.Config.Sensitive, |
107c1cdb | 123 | Expr: n.Config.Expr, |
bae9f6d2 JC |
124 | }, |
125 | }, | |
126 | }, | |
127 | } | |
128 | } | |
15c0b25d | 129 | |
107c1cdb ND |
130 | // dag.GraphNodeDotter impl. |
131 | func (n *NodeApplyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { | |
132 | return &dag.DotNode{ | |
133 | Name: name, | |
134 | Attrs: map[string]string{ | |
135 | "label": n.Name(), | |
136 | "shape": "note", | |
137 | }, | |
138 | } | |
139 | } | |
140 | ||
15c0b25d AP |
141 | // NodeDestroyableOutput represents an output that is "destroybale": |
142 | // its application will remove the output from the state. | |
143 | type NodeDestroyableOutput struct { | |
107c1cdb ND |
144 | Addr addrs.AbsOutputValue |
145 | Config *configs.Output // Config is the output in the config | |
15c0b25d AP |
146 | } |
147 | ||
107c1cdb ND |
148 | var ( |
149 | _ GraphNodeSubPath = (*NodeDestroyableOutput)(nil) | |
150 | _ RemovableIfNotTargeted = (*NodeDestroyableOutput)(nil) | |
151 | _ GraphNodeTargetDownstream = (*NodeDestroyableOutput)(nil) | |
152 | _ GraphNodeReferencer = (*NodeDestroyableOutput)(nil) | |
153 | _ GraphNodeEvalable = (*NodeDestroyableOutput)(nil) | |
154 | _ dag.GraphNodeDotter = (*NodeDestroyableOutput)(nil) | |
155 | ) | |
15c0b25d | 156 | |
107c1cdb ND |
157 | func (n *NodeDestroyableOutput) Name() string { |
158 | return fmt.Sprintf("%s (destroy)", n.Addr.String()) | |
15c0b25d AP |
159 | } |
160 | ||
161 | // GraphNodeSubPath | |
107c1cdb ND |
162 | func (n *NodeDestroyableOutput) Path() addrs.ModuleInstance { |
163 | return n.Addr.Module | |
15c0b25d AP |
164 | } |
165 | ||
166 | // RemovableIfNotTargeted | |
167 | func (n *NodeDestroyableOutput) RemoveIfNotTargeted() bool { | |
168 | // We need to add this so that this node will be removed if | |
169 | // it isn't targeted or a dependency of a target. | |
170 | return true | |
171 | } | |
172 | ||
173 | // This will keep the destroy node in the graph if its corresponding output | |
174 | // node is also in the destroy graph. | |
175 | func (n *NodeDestroyableOutput) TargetDownstream(targetedDeps, untargetedDeps *dag.Set) bool { | |
176 | return true | |
177 | } | |
178 | ||
179 | // GraphNodeReferencer | |
107c1cdb ND |
180 | func (n *NodeDestroyableOutput) References() []*addrs.Reference { |
181 | return referencesForOutput(n.Config) | |
15c0b25d AP |
182 | } |
183 | ||
184 | // GraphNodeEvalable | |
185 | func (n *NodeDestroyableOutput) EvalTree() EvalNode { | |
186 | return &EvalDeleteOutput{ | |
107c1cdb ND |
187 | Addr: n.Addr.OutputValue, |
188 | } | |
189 | } | |
190 | ||
191 | // dag.GraphNodeDotter impl. | |
192 | func (n *NodeDestroyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { | |
193 | return &dag.DotNode{ | |
194 | Name: name, | |
195 | Attrs: map[string]string{ | |
196 | "label": n.Name(), | |
197 | "shape": "note", | |
198 | }, | |
15c0b25d AP |
199 | } |
200 | } |