github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/node_output.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 7 "github.com/hashicorp/terraform-plugin-sdk/internal/configs" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/dag" 9 "github.com/hashicorp/terraform-plugin-sdk/internal/lang" 10 ) 11 12 // NodeApplyableOutput represents an output that is "applyable": 13 // it is ready to be applied. 14 type NodeApplyableOutput struct { 15 Addr addrs.AbsOutputValue 16 Config *configs.Output // Config is the output in the config 17 } 18 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 ) 29 30 func (n *NodeApplyableOutput) Name() string { 31 return n.Addr.String() 32 } 33 34 // GraphNodeSubPath 35 func (n *NodeApplyableOutput) Path() addrs.ModuleInstance { 36 return n.Addr.Module 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 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 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 90 // GraphNodeReferenceable 91 func (n *NodeApplyableOutput) ReferenceableAddrs() []addrs.Referenceable { 92 return referenceableAddrsForOutput(n.Addr) 93 } 94 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 101 } 102 refs := make([]*addrs.Reference, 0, l) 103 refs = append(refs, impRefs...) 104 refs = append(refs, expRefs...) 105 return refs 106 107 } 108 109 // GraphNodeReferencer 110 func (n *NodeApplyableOutput) References() []*addrs.Reference { 111 return appendResourceDestroyReferences(referencesForOutput(n.Config)) 112 } 113 114 // GraphNodeEvalable 115 func (n *NodeApplyableOutput) EvalTree() EvalNode { 116 return &EvalSequence{ 117 Nodes: []EvalNode{ 118 &EvalOpFilter{ 119 Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy}, 120 Node: &EvalWriteOutput{ 121 Addr: n.Addr.OutputValue, 122 Sensitive: n.Config.Sensitive, 123 Expr: n.Config.Expr, 124 }, 125 }, 126 }, 127 } 128 } 129 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 141 // NodeDestroyableOutput represents an output that is "destroybale": 142 // its application will remove the output from the state. 143 type NodeDestroyableOutput struct { 144 Addr addrs.AbsOutputValue 145 Config *configs.Output // Config is the output in the config 146 } 147 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 ) 156 157 func (n *NodeDestroyableOutput) Name() string { 158 return fmt.Sprintf("%s (destroy)", n.Addr.String()) 159 } 160 161 // GraphNodeSubPath 162 func (n *NodeDestroyableOutput) Path() addrs.ModuleInstance { 163 return n.Addr.Module 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 180 func (n *NodeDestroyableOutput) References() []*addrs.Reference { 181 return referencesForOutput(n.Config) 182 } 183 184 // GraphNodeEvalable 185 func (n *NodeDestroyableOutput) EvalTree() EvalNode { 186 return &EvalDeleteOutput{ 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 }, 199 } 200 }