github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/node_resource_abstract.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/terraform/internal/addrs" 8 "github.com/hashicorp/terraform/internal/configs" 9 "github.com/hashicorp/terraform/internal/configs/configschema" 10 "github.com/hashicorp/terraform/internal/dag" 11 "github.com/hashicorp/terraform/internal/lang" 12 "github.com/hashicorp/terraform/internal/states" 13 "github.com/hashicorp/terraform/internal/tfdiags" 14 ) 15 16 // ConcreteResourceNodeFunc is a callback type used to convert an 17 // abstract resource to a concrete one of some type. 18 type ConcreteResourceNodeFunc func(*NodeAbstractResource) dag.Vertex 19 20 // GraphNodeConfigResource is implemented by any nodes that represent a resource. 21 // The type of operation cannot be assumed, only that this node represents 22 // the given resource. 23 type GraphNodeConfigResource interface { 24 ResourceAddr() addrs.ConfigResource 25 } 26 27 // ConcreteResourceInstanceNodeFunc is a callback type used to convert an 28 // abstract resource instance to a concrete one of some type. 29 type ConcreteResourceInstanceNodeFunc func(*NodeAbstractResourceInstance) dag.Vertex 30 31 // GraphNodeResourceInstance is implemented by any nodes that represent 32 // a resource instance. A single resource may have multiple instances if, 33 // for example, the "count" or "for_each" argument is used for it in 34 // configuration. 35 type GraphNodeResourceInstance interface { 36 ResourceInstanceAddr() addrs.AbsResourceInstance 37 38 // StateDependencies returns any inter-resource dependencies that are 39 // stored in the state. 40 StateDependencies() []addrs.ConfigResource 41 } 42 43 // NodeAbstractResource represents a resource that has no associated 44 // operations. It registers all the interfaces for a resource that common 45 // across multiple operation types. 46 type NodeAbstractResource struct { 47 Addr addrs.ConfigResource 48 49 // The fields below will be automatically set using the Attach 50 // interfaces if you're running those transforms, but also be explicitly 51 // set if you already have that information. 52 53 Schema *configschema.Block // Schema for processing the configuration body 54 SchemaVersion uint64 // Schema version of "Schema", as decided by the provider 55 Config *configs.Resource // Config is the resource in the config 56 57 // ProviderMetas is the provider_meta configs for the module this resource belongs to 58 ProviderMetas map[addrs.Provider]*configs.ProviderMeta 59 60 ProvisionerSchemas map[string]*configschema.Block 61 62 // Set from GraphNodeTargetable 63 Targets []addrs.Targetable 64 65 // Set from AttachDataResourceDependsOn 66 dependsOn []addrs.ConfigResource 67 forceDependsOn bool 68 69 // The address of the provider this resource will use 70 ResolvedProvider addrs.AbsProviderConfig 71 72 // This resource may expand into instances which need to be imported. 73 importTargets []*ImportTarget 74 } 75 76 var ( 77 _ GraphNodeReferenceable = (*NodeAbstractResource)(nil) 78 _ GraphNodeReferencer = (*NodeAbstractResource)(nil) 79 _ GraphNodeProviderConsumer = (*NodeAbstractResource)(nil) 80 _ GraphNodeProvisionerConsumer = (*NodeAbstractResource)(nil) 81 _ GraphNodeConfigResource = (*NodeAbstractResource)(nil) 82 _ GraphNodeAttachResourceConfig = (*NodeAbstractResource)(nil) 83 _ GraphNodeAttachResourceSchema = (*NodeAbstractResource)(nil) 84 _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResource)(nil) 85 _ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil) 86 _ GraphNodeTargetable = (*NodeAbstractResource)(nil) 87 _ graphNodeAttachDataResourceDependsOn = (*NodeAbstractResource)(nil) 88 _ dag.GraphNodeDotter = (*NodeAbstractResource)(nil) 89 ) 90 91 // NewNodeAbstractResource creates an abstract resource graph node for 92 // the given absolute resource address. 93 func NewNodeAbstractResource(addr addrs.ConfigResource) *NodeAbstractResource { 94 return &NodeAbstractResource{ 95 Addr: addr, 96 } 97 } 98 99 var ( 100 _ GraphNodeModuleInstance = (*NodeAbstractResourceInstance)(nil) 101 _ GraphNodeReferenceable = (*NodeAbstractResourceInstance)(nil) 102 _ GraphNodeReferencer = (*NodeAbstractResourceInstance)(nil) 103 _ GraphNodeProviderConsumer = (*NodeAbstractResourceInstance)(nil) 104 _ GraphNodeProvisionerConsumer = (*NodeAbstractResourceInstance)(nil) 105 _ GraphNodeConfigResource = (*NodeAbstractResourceInstance)(nil) 106 _ GraphNodeResourceInstance = (*NodeAbstractResourceInstance)(nil) 107 _ GraphNodeAttachResourceState = (*NodeAbstractResourceInstance)(nil) 108 _ GraphNodeAttachResourceConfig = (*NodeAbstractResourceInstance)(nil) 109 _ GraphNodeAttachResourceSchema = (*NodeAbstractResourceInstance)(nil) 110 _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResourceInstance)(nil) 111 _ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResourceInstance)(nil) 112 _ GraphNodeTargetable = (*NodeAbstractResourceInstance)(nil) 113 _ dag.GraphNodeDotter = (*NodeAbstractResourceInstance)(nil) 114 ) 115 116 func (n *NodeAbstractResource) Name() string { 117 return n.ResourceAddr().String() 118 } 119 120 // GraphNodeModulePath 121 func (n *NodeAbstractResource) ModulePath() addrs.Module { 122 return n.Addr.Module 123 } 124 125 // GraphNodeReferenceable 126 func (n *NodeAbstractResource) ReferenceableAddrs() []addrs.Referenceable { 127 return []addrs.Referenceable{n.Addr.Resource} 128 } 129 130 func (n *NodeAbstractResource) Import(addr *ImportTarget) { 131 132 } 133 134 // GraphNodeReferencer 135 func (n *NodeAbstractResource) References() []*addrs.Reference { 136 // If we have a config then we prefer to use that. 137 if c := n.Config; c != nil { 138 var result []*addrs.Reference 139 140 result = append(result, n.DependsOn()...) 141 142 if n.Schema == nil { 143 // Should never happen, but we'll log if it does so that we can 144 // see this easily when debugging. 145 log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name()) 146 } 147 148 refs, _ := lang.ReferencesInExpr(c.Count) 149 result = append(result, refs...) 150 refs, _ = lang.ReferencesInExpr(c.ForEach) 151 result = append(result, refs...) 152 153 for _, expr := range c.TriggersReplacement { 154 refs, _ = lang.ReferencesInExpr(expr) 155 result = append(result, refs...) 156 } 157 158 // ReferencesInBlock() requires a schema 159 if n.Schema != nil { 160 refs, _ = lang.ReferencesInBlock(c.Config, n.Schema) 161 result = append(result, refs...) 162 } 163 164 if c.Managed != nil { 165 if c.Managed.Connection != nil { 166 refs, _ = lang.ReferencesInBlock(c.Managed.Connection.Config, connectionBlockSupersetSchema) 167 result = append(result, refs...) 168 } 169 170 for _, p := range c.Managed.Provisioners { 171 if p.When != configs.ProvisionerWhenCreate { 172 continue 173 } 174 if p.Connection != nil { 175 refs, _ = lang.ReferencesInBlock(p.Connection.Config, connectionBlockSupersetSchema) 176 result = append(result, refs...) 177 } 178 179 schema := n.ProvisionerSchemas[p.Type] 180 if schema == nil { 181 log.Printf("[WARN] no schema for provisioner %q is attached to %s, so provisioner block references cannot be detected", p.Type, n.Name()) 182 } 183 refs, _ = lang.ReferencesInBlock(p.Config, schema) 184 result = append(result, refs...) 185 } 186 } 187 188 for _, check := range c.Preconditions { 189 refs, _ := lang.ReferencesInExpr(check.Condition) 190 result = append(result, refs...) 191 refs, _ = lang.ReferencesInExpr(check.ErrorMessage) 192 result = append(result, refs...) 193 } 194 for _, check := range c.Postconditions { 195 refs, _ := lang.ReferencesInExpr(check.Condition) 196 result = append(result, refs...) 197 refs, _ = lang.ReferencesInExpr(check.ErrorMessage) 198 result = append(result, refs...) 199 } 200 201 return result 202 } 203 204 // Otherwise, we have no references. 205 return nil 206 } 207 208 func (n *NodeAbstractResource) DependsOn() []*addrs.Reference { 209 var result []*addrs.Reference 210 if c := n.Config; c != nil { 211 212 for _, traversal := range c.DependsOn { 213 ref, diags := addrs.ParseRef(traversal) 214 if diags.HasErrors() { 215 // We ignore this here, because this isn't a suitable place to return 216 // errors. This situation should be caught and rejected during 217 // validation. 218 log.Printf("[ERROR] Can't parse %#v from depends_on as reference: %s", traversal, diags.Err()) 219 continue 220 } 221 222 result = append(result, ref) 223 } 224 } 225 return result 226 } 227 228 func (n *NodeAbstractResource) SetProvider(p addrs.AbsProviderConfig) { 229 n.ResolvedProvider = p 230 } 231 232 // GraphNodeProviderConsumer 233 func (n *NodeAbstractResource) ProvidedBy() (addrs.ProviderConfig, bool) { 234 // If we have a config we prefer that above all else 235 if n.Config != nil { 236 relAddr := n.Config.ProviderConfigAddr() 237 return addrs.LocalProviderConfig{ 238 LocalName: relAddr.LocalName, 239 Alias: relAddr.Alias, 240 }, false 241 } 242 243 // No provider configuration found; return a default address 244 return addrs.AbsProviderConfig{ 245 Provider: n.Provider(), 246 Module: n.ModulePath(), 247 }, false 248 } 249 250 // GraphNodeProviderConsumer 251 func (n *NodeAbstractResource) Provider() addrs.Provider { 252 if n.Config != nil { 253 return n.Config.Provider 254 } 255 return addrs.ImpliedProviderForUnqualifiedType(n.Addr.Resource.ImpliedProvider()) 256 } 257 258 // GraphNodeProvisionerConsumer 259 func (n *NodeAbstractResource) ProvisionedBy() []string { 260 // If we have no configuration, then we have no provisioners 261 if n.Config == nil || n.Config.Managed == nil { 262 return nil 263 } 264 265 // Build the list of provisioners we need based on the configuration. 266 // It is okay to have duplicates here. 267 result := make([]string, len(n.Config.Managed.Provisioners)) 268 for i, p := range n.Config.Managed.Provisioners { 269 result[i] = p.Type 270 } 271 272 return result 273 } 274 275 // GraphNodeProvisionerConsumer 276 func (n *NodeAbstractResource) AttachProvisionerSchema(name string, schema *configschema.Block) { 277 if n.ProvisionerSchemas == nil { 278 n.ProvisionerSchemas = make(map[string]*configschema.Block) 279 } 280 n.ProvisionerSchemas[name] = schema 281 } 282 283 // GraphNodeResource 284 func (n *NodeAbstractResource) ResourceAddr() addrs.ConfigResource { 285 return n.Addr 286 } 287 288 // GraphNodeTargetable 289 func (n *NodeAbstractResource) SetTargets(targets []addrs.Targetable) { 290 n.Targets = targets 291 } 292 293 // graphNodeAttachDataResourceDependsOn 294 func (n *NodeAbstractResource) AttachDataResourceDependsOn(deps []addrs.ConfigResource, force bool) { 295 n.dependsOn = deps 296 n.forceDependsOn = force 297 } 298 299 // GraphNodeAttachResourceConfig 300 func (n *NodeAbstractResource) AttachResourceConfig(c *configs.Resource) { 301 n.Config = c 302 } 303 304 // GraphNodeAttachResourceSchema impl 305 func (n *NodeAbstractResource) AttachResourceSchema(schema *configschema.Block, version uint64) { 306 n.Schema = schema 307 n.SchemaVersion = version 308 } 309 310 // GraphNodeAttachProviderMetaConfigs impl 311 func (n *NodeAbstractResource) AttachProviderMetaConfigs(c map[addrs.Provider]*configs.ProviderMeta) { 312 n.ProviderMetas = c 313 } 314 315 // GraphNodeDotter impl. 316 func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { 317 return &dag.DotNode{ 318 Name: name, 319 Attrs: map[string]string{ 320 "label": n.Name(), 321 "shape": "box", 322 }, 323 } 324 } 325 326 // writeResourceState ensures that a suitable resource-level state record is 327 // present in the state, if that's required for the "each mode" of that 328 // resource. 329 // 330 // This is important primarily for the situation where count = 0, since this 331 // eval is the only change we get to set the resource "each mode" to list 332 // in that case, allowing expression evaluation to see it as a zero-element list 333 // rather than as not set at all. 334 func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.AbsResource) (diags tfdiags.Diagnostics) { 335 state := ctx.State() 336 337 // We'll record our expansion decision in the shared "expander" object 338 // so that later operations (i.e. DynamicExpand and expression evaluation) 339 // can refer to it. Since this node represents the abstract module, we need 340 // to expand the module here to create all resources. 341 expander := ctx.InstanceExpander() 342 343 switch { 344 case n.Config.Count != nil: 345 count, countDiags := evaluateCountExpression(n.Config.Count, ctx) 346 diags = diags.Append(countDiags) 347 if countDiags.HasErrors() { 348 return diags 349 } 350 351 state.SetResourceProvider(addr, n.ResolvedProvider) 352 expander.SetResourceCount(addr.Module, n.Addr.Resource, count) 353 354 case n.Config.ForEach != nil: 355 forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx) 356 diags = diags.Append(forEachDiags) 357 if forEachDiags.HasErrors() { 358 return diags 359 } 360 361 // This method takes care of all of the business logic of updating this 362 // while ensuring that any existing instances are preserved, etc. 363 state.SetResourceProvider(addr, n.ResolvedProvider) 364 expander.SetResourceForEach(addr.Module, n.Addr.Resource, forEach) 365 366 default: 367 state.SetResourceProvider(addr, n.ResolvedProvider) 368 expander.SetResourceSingle(addr.Module, n.Addr.Resource) 369 } 370 371 return diags 372 } 373 374 // readResourceInstanceState reads the current object for a specific instance in 375 // the state. 376 func (n *NodeAbstractResource) readResourceInstanceState(ctx EvalContext, addr addrs.AbsResourceInstance) (*states.ResourceInstanceObject, tfdiags.Diagnostics) { 377 var diags tfdiags.Diagnostics 378 provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) 379 if err != nil { 380 diags = diags.Append(err) 381 return nil, diags 382 } 383 384 log.Printf("[TRACE] readResourceInstanceState: reading state for %s", addr) 385 386 src := ctx.State().ResourceInstanceObject(addr, states.CurrentGen) 387 if src == nil { 388 // Presumably we only have deposed objects, then. 389 log.Printf("[TRACE] readResourceInstanceState: no state present for %s", addr) 390 return nil, nil 391 } 392 393 schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource()) 394 if schema == nil { 395 // Shouldn't happen since we should've failed long ago if no schema is present 396 return nil, diags.Append(fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr)) 397 } 398 src, upgradeDiags := upgradeResourceState(addr, provider, src, schema, currentVersion) 399 if n.Config != nil { 400 upgradeDiags = upgradeDiags.InConfigBody(n.Config.Config, addr.String()) 401 } 402 diags = diags.Append(upgradeDiags) 403 if diags.HasErrors() { 404 return nil, diags 405 } 406 407 obj, err := src.Decode(schema.ImpliedType()) 408 if err != nil { 409 diags = diags.Append(err) 410 } 411 412 return obj, diags 413 } 414 415 // readResourceInstanceStateDeposed reads the deposed object for a specific 416 // instance in the state. 417 func (n *NodeAbstractResource) readResourceInstanceStateDeposed(ctx EvalContext, addr addrs.AbsResourceInstance, key states.DeposedKey) (*states.ResourceInstanceObject, tfdiags.Diagnostics) { 418 var diags tfdiags.Diagnostics 419 provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) 420 if err != nil { 421 diags = diags.Append(err) 422 return nil, diags 423 } 424 425 if key == states.NotDeposed { 426 return nil, diags.Append(fmt.Errorf("readResourceInstanceStateDeposed used with no instance key; this is a bug in Terraform and should be reported")) 427 } 428 429 log.Printf("[TRACE] readResourceInstanceStateDeposed: reading state for %s deposed object %s", addr, key) 430 431 src := ctx.State().ResourceInstanceObject(addr, key) 432 if src == nil { 433 // Presumably we only have deposed objects, then. 434 log.Printf("[TRACE] readResourceInstanceStateDeposed: no state present for %s deposed object %s", addr, key) 435 return nil, diags 436 } 437 438 schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource()) 439 if schema == nil { 440 // Shouldn't happen since we should've failed long ago if no schema is present 441 return nil, diags.Append(fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr)) 442 443 } 444 445 src, upgradeDiags := upgradeResourceState(addr, provider, src, schema, currentVersion) 446 if n.Config != nil { 447 upgradeDiags = upgradeDiags.InConfigBody(n.Config.Config, addr.String()) 448 } 449 diags = diags.Append(upgradeDiags) 450 if diags.HasErrors() { 451 // Note that we don't have any channel to return warnings here. We'll 452 // accept that for now since warnings during a schema upgrade would 453 // be pretty weird anyway, since this operation is supposed to seem 454 // invisible to the user. 455 return nil, diags 456 } 457 458 obj, err := src.Decode(schema.ImpliedType()) 459 if err != nil { 460 diags = diags.Append(err) 461 } 462 463 return obj, diags 464 } 465 466 // graphNodesAreResourceInstancesInDifferentInstancesOfSameModule is an 467 // annoyingly-task-specific helper function that returns true if and only if 468 // the following conditions hold: 469 // - Both of the given vertices represent specific resource instances, as 470 // opposed to unexpanded resources or any other non-resource-related object. 471 // - The module instance addresses for both of the resource instances belong 472 // to the same static module. 473 // - The module instance addresses for both of the resource instances are 474 // not equal, indicating that they belong to different instances of the 475 // same module. 476 // 477 // This result can be used as a way to compensate for the effects of 478 // conservative analysis passes in our graph builders which make their 479 // decisions based only on unexpanded addresses, often so that they can behave 480 // correctly for interactions between expanded and not-yet-expanded objects. 481 // 482 // Callers of this helper function will typically skip adding an edge between 483 // the two given nodes if this function returns true. 484 func graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(a, b dag.Vertex) bool { 485 aRI, aOK := a.(GraphNodeResourceInstance) 486 bRI, bOK := b.(GraphNodeResourceInstance) 487 if !(aOK && bOK) { 488 return false 489 } 490 aModInst := aRI.ResourceInstanceAddr().Module 491 bModInst := bRI.ResourceInstanceAddr().Module 492 aMod := aModInst.Module() 493 bMod := bModInst.Module() 494 if !aMod.Equal(bMod) { 495 return false 496 } 497 return !aModInst.Equal(bModInst) 498 }