github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/node_resource_plan.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 8 "github.com/muratcelep/terraform/not-internal/addrs" 9 "github.com/muratcelep/terraform/not-internal/dag" 10 "github.com/muratcelep/terraform/not-internal/states" 11 "github.com/muratcelep/terraform/not-internal/tfdiags" 12 ) 13 14 // nodeExpandPlannableResource handles the first layer of resource 15 // expansion. We need this extra layer so DynamicExpand is called twice for 16 // the resource, the first to expand the Resource for each module instance, and 17 // the second to expand each ResourceInstance for the expanded Resources. 18 type nodeExpandPlannableResource struct { 19 *NodeAbstractResource 20 21 // ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD 22 // during graph construction, if dependencies require us to force this 23 // on regardless of what the configuration says. 24 ForceCreateBeforeDestroy *bool 25 26 // skipRefresh indicates that we should skip refreshing individual instances 27 skipRefresh bool 28 29 // skipPlanChanges indicates we should skip trying to plan change actions 30 // for any instances. 31 skipPlanChanges bool 32 33 // forceReplace are resource instance addresses where the user wants to 34 // force generating a replace action. This set isn't pre-filtered, so 35 // it might contain addresses that have nothing to do with the resource 36 // that this node represents, which the node itself must therefore ignore. 37 forceReplace []addrs.AbsResourceInstance 38 39 // We attach dependencies to the Resource during refresh, since the 40 // instances are instantiated during DynamicExpand. 41 dependencies []addrs.ConfigResource 42 } 43 44 var ( 45 _ GraphNodeDestroyerCBD = (*nodeExpandPlannableResource)(nil) 46 _ GraphNodeDynamicExpandable = (*nodeExpandPlannableResource)(nil) 47 _ GraphNodeReferenceable = (*nodeExpandPlannableResource)(nil) 48 _ GraphNodeReferencer = (*nodeExpandPlannableResource)(nil) 49 _ GraphNodeConfigResource = (*nodeExpandPlannableResource)(nil) 50 _ GraphNodeAttachResourceConfig = (*nodeExpandPlannableResource)(nil) 51 _ GraphNodeAttachDependencies = (*nodeExpandPlannableResource)(nil) 52 _ GraphNodeTargetable = (*nodeExpandPlannableResource)(nil) 53 ) 54 55 func (n *nodeExpandPlannableResource) Name() string { 56 return n.NodeAbstractResource.Name() + " (expand)" 57 } 58 59 // GraphNodeAttachDependencies 60 func (n *nodeExpandPlannableResource) AttachDependencies(deps []addrs.ConfigResource) { 61 n.dependencies = deps 62 } 63 64 // GraphNodeDestroyerCBD 65 func (n *nodeExpandPlannableResource) CreateBeforeDestroy() bool { 66 if n.ForceCreateBeforeDestroy != nil { 67 return *n.ForceCreateBeforeDestroy 68 } 69 70 // If we have no config, we just assume no 71 if n.Config == nil || n.Config.Managed == nil { 72 return false 73 } 74 75 return n.Config.Managed.CreateBeforeDestroy 76 } 77 78 // GraphNodeDestroyerCBD 79 func (n *nodeExpandPlannableResource) ModifyCreateBeforeDestroy(v bool) error { 80 n.ForceCreateBeforeDestroy = &v 81 return nil 82 } 83 84 func (n *nodeExpandPlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { 85 var g Graph 86 87 expander := ctx.InstanceExpander() 88 moduleInstances := expander.ExpandModule(n.Addr.Module) 89 90 // Add the current expanded resource to the graph 91 for _, module := range moduleInstances { 92 resAddr := n.Addr.Resource.Absolute(module) 93 g.Add(&NodePlannableResource{ 94 NodeAbstractResource: n.NodeAbstractResource, 95 Addr: resAddr, 96 ForceCreateBeforeDestroy: n.ForceCreateBeforeDestroy, 97 dependencies: n.dependencies, 98 skipRefresh: n.skipRefresh, 99 skipPlanChanges: n.skipPlanChanges, 100 forceReplace: n.forceReplace, 101 }) 102 } 103 104 // Lock the state while we inspect it 105 state := ctx.State().Lock() 106 defer ctx.State().Unlock() 107 108 var orphans []*states.Resource 109 for _, res := range state.Resources(n.Addr) { 110 found := false 111 for _, m := range moduleInstances { 112 if m.Equal(res.Addr.Module) { 113 found = true 114 break 115 } 116 } 117 // Address form state was not found in the current config 118 if !found { 119 orphans = append(orphans, res) 120 } 121 } 122 123 // The concrete resource factory we'll use for orphans 124 concreteResourceOrphan := func(a *NodeAbstractResourceInstance) *NodePlannableResourceInstanceOrphan { 125 // Add the config and state since we don't do that via transforms 126 a.Config = n.Config 127 a.ResolvedProvider = n.ResolvedProvider 128 a.Schema = n.Schema 129 a.ProvisionerSchemas = n.ProvisionerSchemas 130 a.ProviderMetas = n.ProviderMetas 131 a.Dependencies = n.dependencies 132 133 return &NodePlannableResourceInstanceOrphan{ 134 NodeAbstractResourceInstance: a, 135 skipRefresh: n.skipRefresh, 136 skipPlanChanges: n.skipPlanChanges, 137 } 138 } 139 140 for _, res := range orphans { 141 for key := range res.Instances { 142 addr := res.Addr.Instance(key) 143 abs := NewNodeAbstractResourceInstance(addr) 144 abs.AttachResourceState(res) 145 n := concreteResourceOrphan(abs) 146 g.Add(n) 147 } 148 } 149 150 return &g, nil 151 } 152 153 // NodePlannableResource represents a resource that is "plannable": 154 // it is ready to be planned in order to create a diff. 155 type NodePlannableResource struct { 156 *NodeAbstractResource 157 158 Addr addrs.AbsResource 159 160 // ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD 161 // during graph construction, if dependencies require us to force this 162 // on regardless of what the configuration says. 163 ForceCreateBeforeDestroy *bool 164 165 // skipRefresh indicates that we should skip refreshing individual instances 166 skipRefresh bool 167 168 // skipPlanChanges indicates we should skip trying to plan change actions 169 // for any instances. 170 skipPlanChanges bool 171 172 // forceReplace are resource instance addresses where the user wants to 173 // force generating a replace action. This set isn't pre-filtered, so 174 // it might contain addresses that have nothing to do with the resource 175 // that this node represents, which the node itself must therefore ignore. 176 forceReplace []addrs.AbsResourceInstance 177 178 dependencies []addrs.ConfigResource 179 } 180 181 var ( 182 _ GraphNodeModuleInstance = (*NodePlannableResource)(nil) 183 _ GraphNodeDestroyerCBD = (*NodePlannableResource)(nil) 184 _ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil) 185 _ GraphNodeReferenceable = (*NodePlannableResource)(nil) 186 _ GraphNodeReferencer = (*NodePlannableResource)(nil) 187 _ GraphNodeConfigResource = (*NodePlannableResource)(nil) 188 _ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil) 189 ) 190 191 func (n *NodePlannableResource) Path() addrs.ModuleInstance { 192 return n.Addr.Module 193 } 194 195 func (n *NodePlannableResource) Name() string { 196 return n.Addr.String() 197 } 198 199 // GraphNodeExecutable 200 func (n *NodePlannableResource) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { 201 var diags tfdiags.Diagnostics 202 203 if n.Config == nil { 204 // Nothing to do, then. 205 log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", n.Name()) 206 return diags 207 } 208 209 // writeResourceState is responsible for informing the expander of what 210 // repetition mode this resource has, which allows expander.ExpandResource 211 // to work below. 212 moreDiags := n.writeResourceState(ctx, n.Addr) 213 diags = diags.Append(moreDiags) 214 if moreDiags.HasErrors() { 215 return diags 216 } 217 218 // Before we expand our resource into potentially many resource instances, 219 // we'll verify that any mention of this resource in n.forceReplace is 220 // consistent with the repetition mode of the resource. In other words, 221 // we're aiming to catch a situation where naming a particular resource 222 // instance would require an instance key but the given address has none. 223 expander := ctx.InstanceExpander() 224 instanceAddrs := expander.ExpandResource(n.ResourceAddr().Absolute(ctx.Path())) 225 226 // If there's a number of instances other than 1 then we definitely need 227 // an index. 228 mustHaveIndex := len(instanceAddrs) != 1 229 // If there's only one instance then we might still need an index, if the 230 // instance address has one. 231 if len(instanceAddrs) == 1 && instanceAddrs[0].Resource.Key != addrs.NoKey { 232 mustHaveIndex = true 233 } 234 if mustHaveIndex { 235 for _, candidateAddr := range n.forceReplace { 236 if candidateAddr.Resource.Key == addrs.NoKey { 237 if n.Addr.Resource.Equal(candidateAddr.Resource.Resource) { 238 switch { 239 case len(instanceAddrs) == 0: 240 // In this case there _are_ no instances to replace, so 241 // there isn't any alternative address for us to suggest. 242 diags = diags.Append(tfdiags.Sourceless( 243 tfdiags.Warning, 244 "Incompletely-matched force-replace resource instance", 245 fmt.Sprintf( 246 "Your force-replace request for %s doesn't match any resource instances because this resource doesn't have any instances.", 247 candidateAddr, 248 ), 249 )) 250 case len(instanceAddrs) == 1: 251 diags = diags.Append(tfdiags.Sourceless( 252 tfdiags.Warning, 253 "Incompletely-matched force-replace resource instance", 254 fmt.Sprintf( 255 "Your force-replace request for %s doesn't match any resource instances because it lacks an instance key.\n\nTo force replacement of the single declared instance, use the following option instead:\n -replace=%q", 256 candidateAddr, instanceAddrs[0], 257 ), 258 )) 259 default: 260 var possibleValidOptions strings.Builder 261 for _, addr := range instanceAddrs { 262 fmt.Fprintf(&possibleValidOptions, "\n -replace=%q", addr) 263 } 264 265 diags = diags.Append(tfdiags.Sourceless( 266 tfdiags.Warning, 267 "Incompletely-matched force-replace resource instance", 268 fmt.Sprintf( 269 "Your force-replace request for %s doesn't match any resource instances because it lacks an instance key.\n\nTo force replacement of particular instances, use one or more of the following options instead:%s", 270 candidateAddr, possibleValidOptions.String(), 271 ), 272 )) 273 } 274 } 275 } 276 } 277 } 278 // NOTE: The actual interpretation of n.forceReplace to produce replace 279 // actions is in NodeAbstractResourceInstance.plan, because we must do so 280 // on a per-instance basis rather than for the whole resource. 281 282 return diags 283 } 284 285 // GraphNodeDestroyerCBD 286 func (n *NodePlannableResource) CreateBeforeDestroy() bool { 287 if n.ForceCreateBeforeDestroy != nil { 288 return *n.ForceCreateBeforeDestroy 289 } 290 291 // If we have no config, we just assume no 292 if n.Config == nil || n.Config.Managed == nil { 293 return false 294 } 295 296 return n.Config.Managed.CreateBeforeDestroy 297 } 298 299 // GraphNodeDestroyerCBD 300 func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error { 301 n.ForceCreateBeforeDestroy = &v 302 return nil 303 } 304 305 // GraphNodeDynamicExpandable 306 func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { 307 var diags tfdiags.Diagnostics 308 309 // Our instance expander should already have been informed about the 310 // expansion of this resource and of all of its containing modules, so 311 // it can tell us which instance addresses we need to process. 312 expander := ctx.InstanceExpander() 313 instanceAddrs := expander.ExpandResource(n.ResourceAddr().Absolute(ctx.Path())) 314 315 // Our graph transformers require access to the full state, so we'll 316 // temporarily lock it while we work on this. 317 state := ctx.State().Lock() 318 defer ctx.State().Unlock() 319 320 // The concrete resource factory we'll use 321 concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex { 322 // Add the config and state since we don't do that via transforms 323 a.Config = n.Config 324 a.ResolvedProvider = n.ResolvedProvider 325 a.Schema = n.Schema 326 a.ProvisionerSchemas = n.ProvisionerSchemas 327 a.ProviderMetas = n.ProviderMetas 328 a.dependsOn = n.dependsOn 329 a.Dependencies = n.dependencies 330 331 return &NodePlannableResourceInstance{ 332 NodeAbstractResourceInstance: a, 333 334 // By the time we're walking, we've figured out whether we need 335 // to force on CreateBeforeDestroy due to dependencies on other 336 // nodes that have it. 337 ForceCreateBeforeDestroy: n.CreateBeforeDestroy(), 338 skipRefresh: n.skipRefresh, 339 skipPlanChanges: n.skipPlanChanges, 340 forceReplace: n.forceReplace, 341 } 342 } 343 344 // The concrete resource factory we'll use for orphans 345 concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex { 346 // Add the config and state since we don't do that via transforms 347 a.Config = n.Config 348 a.ResolvedProvider = n.ResolvedProvider 349 a.Schema = n.Schema 350 a.ProvisionerSchemas = n.ProvisionerSchemas 351 a.ProviderMetas = n.ProviderMetas 352 353 return &NodePlannableResourceInstanceOrphan{ 354 NodeAbstractResourceInstance: a, 355 skipRefresh: n.skipRefresh, 356 skipPlanChanges: n.skipPlanChanges, 357 } 358 } 359 360 // Start creating the steps 361 steps := []GraphTransformer{ 362 // Expand the count or for_each (if present) 363 &ResourceCountTransformer{ 364 Concrete: concreteResource, 365 Schema: n.Schema, 366 Addr: n.ResourceAddr(), 367 InstanceAddrs: instanceAddrs, 368 }, 369 370 // Add the count/for_each orphans 371 &OrphanResourceInstanceCountTransformer{ 372 Concrete: concreteResourceOrphan, 373 Addr: n.Addr, 374 InstanceAddrs: instanceAddrs, 375 State: state, 376 }, 377 378 // Attach the state 379 &AttachStateTransformer{State: state}, 380 381 // Targeting 382 &TargetsTransformer{Targets: n.Targets}, 383 384 // Connect references so ordering is correct 385 &ReferenceTransformer{}, 386 387 // Make sure there is a single root 388 &RootTransformer{}, 389 } 390 391 // Build the graph 392 b := &BasicGraphBuilder{ 393 Steps: steps, 394 Validate: true, 395 Name: "NodePlannableResource", 396 } 397 graph, diags := b.Build(ctx.Path()) 398 return graph, diags.ErrWithWarnings() 399 }