github.com/pulumi/terraform@v1.4.0/pkg/terraform/node_module_expand.go (about) 1 package terraform 2 3 import ( 4 "log" 5 6 "github.com/pulumi/terraform/pkg/addrs" 7 "github.com/pulumi/terraform/pkg/configs" 8 "github.com/pulumi/terraform/pkg/dag" 9 "github.com/pulumi/terraform/pkg/lang" 10 "github.com/pulumi/terraform/pkg/tfdiags" 11 ) 12 13 type ConcreteModuleNodeFunc func(n *nodeExpandModule) dag.Vertex 14 15 // nodeExpandModule represents a module call in the configuration that 16 // might expand into multiple module instances depending on how it is 17 // configured. 18 type nodeExpandModule struct { 19 Addr addrs.Module 20 Config *configs.Module 21 ModuleCall *configs.ModuleCall 22 } 23 24 var ( 25 _ GraphNodeExecutable = (*nodeExpandModule)(nil) 26 _ GraphNodeReferencer = (*nodeExpandModule)(nil) 27 _ GraphNodeReferenceOutside = (*nodeExpandModule)(nil) 28 _ graphNodeExpandsInstances = (*nodeExpandModule)(nil) 29 ) 30 31 func (n *nodeExpandModule) expandsInstances() {} 32 33 func (n *nodeExpandModule) Name() string { 34 return n.Addr.String() + " (expand)" 35 } 36 37 // GraphNodeModulePath implementation 38 func (n *nodeExpandModule) ModulePath() addrs.Module { 39 return n.Addr 40 } 41 42 // GraphNodeReferencer implementation 43 func (n *nodeExpandModule) References() []*addrs.Reference { 44 var refs []*addrs.Reference 45 46 if n.ModuleCall == nil { 47 return nil 48 } 49 50 refs = append(refs, n.DependsOn()...) 51 52 // Expansion only uses the count and for_each expressions, so this 53 // particular graph node only refers to those. 54 // Individual variable values in the module call definition might also 55 // refer to other objects, but that's handled by 56 // NodeApplyableModuleVariable. 57 // 58 // Because our Path method returns the module instance that contains 59 // our call, these references will be correctly interpreted as being 60 // in the calling module's namespace, not the namespaces of any of the 61 // child module instances we might expand to during our evaluation. 62 63 if n.ModuleCall.Count != nil { 64 countRefs, _ := lang.ReferencesInExpr(n.ModuleCall.Count) 65 refs = append(refs, countRefs...) 66 } 67 if n.ModuleCall.ForEach != nil { 68 forEachRefs, _ := lang.ReferencesInExpr(n.ModuleCall.ForEach) 69 refs = append(refs, forEachRefs...) 70 } 71 return refs 72 } 73 74 func (n *nodeExpandModule) DependsOn() []*addrs.Reference { 75 if n.ModuleCall == nil { 76 return nil 77 } 78 79 var refs []*addrs.Reference 80 for _, traversal := range n.ModuleCall.DependsOn { 81 ref, diags := addrs.ParseRef(traversal) 82 if diags.HasErrors() { 83 // We ignore this here, because this isn't a suitable place to return 84 // errors. This situation should be caught and rejected during 85 // validation. 86 log.Printf("[ERROR] Can't parse %#v from depends_on as reference: %s", traversal, diags.Err()) 87 continue 88 } 89 90 refs = append(refs, ref) 91 } 92 93 return refs 94 } 95 96 // GraphNodeReferenceOutside 97 func (n *nodeExpandModule) ReferenceOutside() (selfPath, referencePath addrs.Module) { 98 return n.Addr, n.Addr.Parent() 99 } 100 101 // GraphNodeExecutable 102 func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { 103 expander := ctx.InstanceExpander() 104 _, call := n.Addr.Call() 105 106 // nodeExpandModule itself does not have visibility into how its ancestors 107 // were expanded, so we use the expander here to provide all possible paths 108 // to our module, and register module instances with each of them. 109 for _, module := range expander.ExpandModule(n.Addr.Parent()) { 110 ctx = ctx.WithPath(module) 111 switch { 112 case n.ModuleCall.Count != nil: 113 count, ctDiags := evaluateCountExpression(n.ModuleCall.Count, ctx) 114 diags = diags.Append(ctDiags) 115 if diags.HasErrors() { 116 return diags 117 } 118 expander.SetModuleCount(module, call, count) 119 120 case n.ModuleCall.ForEach != nil: 121 forEach, feDiags := evaluateForEachExpression(n.ModuleCall.ForEach, ctx) 122 diags = diags.Append(feDiags) 123 if diags.HasErrors() { 124 return diags 125 } 126 expander.SetModuleForEach(module, call, forEach) 127 128 default: 129 expander.SetModuleSingle(module, call) 130 } 131 } 132 133 return diags 134 135 } 136 137 // nodeCloseModule represents an expanded module during apply, and is visited 138 // after all other module instance nodes. This node will depend on all module 139 // instance resource and outputs, and anything depending on the module should 140 // wait on this node. 141 // Besides providing a root node for dependency ordering, nodeCloseModule also 142 // cleans up state after all the module nodes have been evaluated, removing 143 // empty resources and modules from the state. 144 // The root module instance also closes any remaining provisioner plugins which 145 // do not have a lifecycle controlled by individual graph nodes. 146 type nodeCloseModule struct { 147 Addr addrs.Module 148 } 149 150 var ( 151 _ GraphNodeReferenceable = (*nodeCloseModule)(nil) 152 _ GraphNodeReferenceOutside = (*nodeCloseModule)(nil) 153 _ GraphNodeExecutable = (*nodeCloseModule)(nil) 154 ) 155 156 func (n *nodeCloseModule) ModulePath() addrs.Module { 157 return n.Addr 158 } 159 160 func (n *nodeCloseModule) ReferenceOutside() (selfPath, referencePath addrs.Module) { 161 return n.Addr.Parent(), n.Addr 162 } 163 164 func (n *nodeCloseModule) ReferenceableAddrs() []addrs.Referenceable { 165 _, call := n.Addr.Call() 166 return []addrs.Referenceable{ 167 call, 168 } 169 } 170 171 func (n *nodeCloseModule) Name() string { 172 if len(n.Addr) == 0 { 173 return "root" 174 } 175 return n.Addr.String() + " (close)" 176 } 177 178 func (n *nodeCloseModule) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { 179 if !n.Addr.IsRoot() { 180 return 181 } 182 183 // If this is the root module, we are cleaning up the walk, so close 184 // any running provisioners 185 diags = diags.Append(ctx.CloseProvisioners()) 186 187 switch op { 188 case walkApply, walkDestroy: 189 state := ctx.State().Lock() 190 defer ctx.State().Unlock() 191 192 for modKey, mod := range state.Modules { 193 // clean out any empty resources 194 for resKey, res := range mod.Resources { 195 if len(res.Instances) == 0 { 196 delete(mod.Resources, resKey) 197 } 198 } 199 200 // empty child modules are always removed 201 if len(mod.Resources) == 0 && !mod.Addr.IsRoot() { 202 delete(state.Modules, modKey) 203 } 204 } 205 return nil 206 default: 207 return nil 208 } 209 } 210 211 // nodeValidateModule wraps a nodeExpand module for validation, ensuring that 212 // no expansion is attempted during evaluation, when count and for_each 213 // expressions may not be known. 214 type nodeValidateModule struct { 215 nodeExpandModule 216 } 217 218 var _ GraphNodeExecutable = (*nodeValidateModule)(nil) 219 220 // GraphNodeEvalable 221 func (n *nodeValidateModule) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { 222 _, call := n.Addr.Call() 223 expander := ctx.InstanceExpander() 224 225 // Modules all evaluate to single instances during validation, only to 226 // create a proper context within which to evaluate. All parent modules 227 // will be a single instance, but still get our address in the expected 228 // manner anyway to ensure they've been registered correctly. 229 for _, module := range expander.ExpandModule(n.Addr.Parent()) { 230 ctx = ctx.WithPath(module) 231 232 // Validate our for_each and count expressions at a basic level 233 // We skip validation on known, because there will be unknown values before 234 // a full expansion, presuming these errors will be caught in later steps 235 switch { 236 case n.ModuleCall.Count != nil: 237 _, countDiags := evaluateCountExpressionValue(n.ModuleCall.Count, ctx) 238 diags = diags.Append(countDiags) 239 240 case n.ModuleCall.ForEach != nil: 241 _, forEachDiags := evaluateForEachExpressionValue(n.ModuleCall.ForEach, ctx, true) 242 diags = diags.Append(forEachDiags) 243 } 244 245 diags = diags.Append(validateDependsOn(ctx, n.ModuleCall.DependsOn)) 246 247 // now set our own mode to single 248 expander.SetModuleSingle(module, call) 249 } 250 251 return diags 252 }