github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/addrs/resource.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package addrs 5 6 import ( 7 "fmt" 8 "strings" 9 ) 10 11 // Resource is an address for a resource block within configuration, which 12 // contains potentially-multiple resource instances if that configuration 13 // block uses "count" or "for_each". 14 type Resource struct { 15 referenceable 16 Mode ResourceMode 17 Type string 18 Name string 19 } 20 21 func (r Resource) String() string { 22 switch r.Mode { 23 case ManagedResourceMode: 24 return fmt.Sprintf("%s.%s", r.Type, r.Name) 25 case DataResourceMode: 26 return fmt.Sprintf("data.%s.%s", r.Type, r.Name) 27 default: 28 // Should never happen, but we'll return a string here rather than 29 // crashing just in case it does. 30 return fmt.Sprintf("<invalid>.%s.%s", r.Type, r.Name) 31 } 32 } 33 34 func (r Resource) Equal(o Resource) bool { 35 return r.Mode == o.Mode && r.Name == o.Name && r.Type == o.Type 36 } 37 38 func (r Resource) Less(o Resource) bool { 39 switch { 40 case r.Mode != o.Mode: 41 return r.Mode == DataResourceMode 42 43 case r.Type != o.Type: 44 return r.Type < o.Type 45 46 case r.Name != o.Name: 47 return r.Name < o.Name 48 49 default: 50 return false 51 } 52 } 53 54 func (r Resource) UniqueKey() UniqueKey { 55 return r // A Resource is its own UniqueKey 56 } 57 58 func (r Resource) uniqueKeySigil() {} 59 60 // Instance produces the address for a specific instance of the receiver 61 // that is idenfied by the given key. 62 func (r Resource) Instance(key InstanceKey) ResourceInstance { 63 return ResourceInstance{ 64 Resource: r, 65 Key: key, 66 } 67 } 68 69 // Absolute returns an AbsResource from the receiver and the given module 70 // instance address. 71 func (r Resource) Absolute(module ModuleInstance) AbsResource { 72 return AbsResource{ 73 Module: module, 74 Resource: r, 75 } 76 } 77 78 // InModule returns a ConfigResource from the receiver and the given module 79 // address. 80 func (r Resource) InModule(module Module) ConfigResource { 81 return ConfigResource{ 82 Module: module, 83 Resource: r, 84 } 85 } 86 87 // ImpliedProvider returns the implied provider type name, for e.g. the "aws" in 88 // "aws_instance" 89 func (r Resource) ImpliedProvider() string { 90 typeName := r.Type 91 if under := strings.Index(typeName, "_"); under != -1 { 92 typeName = typeName[:under] 93 } 94 95 return typeName 96 } 97 98 // ResourceInstance is an address for a specific instance of a resource. 99 // When a resource is defined in configuration with "count" or "for_each" it 100 // produces zero or more instances, which can be addressed using this type. 101 type ResourceInstance struct { 102 referenceable 103 Resource Resource 104 Key InstanceKey 105 } 106 107 func (r ResourceInstance) ContainingResource() Resource { 108 return r.Resource 109 } 110 111 func (r ResourceInstance) String() string { 112 if r.Key == NoKey { 113 return r.Resource.String() 114 } 115 return r.Resource.String() + r.Key.String() 116 } 117 118 func (r ResourceInstance) Equal(o ResourceInstance) bool { 119 return r.Key == o.Key && r.Resource.Equal(o.Resource) 120 } 121 122 func (r ResourceInstance) Less(o ResourceInstance) bool { 123 if !r.Resource.Equal(o.Resource) { 124 return r.Resource.Less(o.Resource) 125 } 126 127 if r.Key != o.Key { 128 return InstanceKeyLess(r.Key, o.Key) 129 } 130 131 return false 132 } 133 134 func (r ResourceInstance) UniqueKey() UniqueKey { 135 return r // A ResourceInstance is its own UniqueKey 136 } 137 138 func (r ResourceInstance) uniqueKeySigil() {} 139 140 // Absolute returns an AbsResourceInstance from the receiver and the given module 141 // instance address. 142 func (r ResourceInstance) Absolute(module ModuleInstance) AbsResourceInstance { 143 return AbsResourceInstance{ 144 Module: module, 145 Resource: r, 146 } 147 } 148 149 // AbsResource is an absolute address for a resource under a given module path. 150 type AbsResource struct { 151 targetable 152 Module ModuleInstance 153 Resource Resource 154 } 155 156 // Resource returns the address of a particular resource within the receiver. 157 func (m ModuleInstance) Resource(mode ResourceMode, typeName string, name string) AbsResource { 158 return AbsResource{ 159 Module: m, 160 Resource: Resource{ 161 Mode: mode, 162 Type: typeName, 163 Name: name, 164 }, 165 } 166 } 167 168 // Instance produces the address for a specific instance of the receiver 169 // that is idenfied by the given key. 170 func (r AbsResource) Instance(key InstanceKey) AbsResourceInstance { 171 return AbsResourceInstance{ 172 Module: r.Module, 173 Resource: r.Resource.Instance(key), 174 } 175 } 176 177 // Config returns the unexpanded ConfigResource for this AbsResource. 178 func (r AbsResource) Config() ConfigResource { 179 return ConfigResource{ 180 Module: r.Module.Module(), 181 Resource: r.Resource, 182 } 183 } 184 185 // TargetContains implements Targetable by returning true if the given other 186 // address is either equal to the receiver or is an instance of the 187 // receiver. 188 func (r AbsResource) TargetContains(other Targetable) bool { 189 switch to := other.(type) { 190 191 case AbsResource: 192 // We'll use our stringification as a cheat-ish way to test for equality. 193 return to.String() == r.String() 194 195 case ConfigResource: 196 // if an absolute resource from parsing a target address contains a 197 // ConfigResource, the string representation will match 198 return to.String() == r.String() 199 200 case AbsResourceInstance: 201 return r.TargetContains(to.ContainingResource()) 202 203 default: 204 return false 205 206 } 207 } 208 209 func (r AbsResource) AddrType() TargetableAddrType { 210 return AbsResourceAddrType 211 } 212 213 func (r AbsResource) String() string { 214 if len(r.Module) == 0 { 215 return r.Resource.String() 216 } 217 return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String()) 218 } 219 220 // AffectedAbsResource returns the AbsResource. 221 func (r AbsResource) AffectedAbsResource() AbsResource { 222 return r 223 } 224 225 func (r AbsResource) Equal(o AbsResource) bool { 226 return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource) 227 } 228 229 func (r AbsResource) Less(o AbsResource) bool { 230 if !r.Module.Equal(o.Module) { 231 return r.Module.Less(o.Module) 232 } 233 234 if !r.Resource.Equal(o.Resource) { 235 return r.Resource.Less(o.Resource) 236 } 237 238 return false 239 } 240 241 func (r AbsResource) absMoveableSigil() { 242 // AbsResource is moveable 243 } 244 245 type absResourceKey string 246 247 func (r absResourceKey) uniqueKeySigil() {} 248 249 func (r AbsResource) UniqueKey() UniqueKey { 250 return absResourceKey(r.String()) 251 } 252 253 // AbsResourceInstance is an absolute address for a resource instance under a 254 // given module path. 255 type AbsResourceInstance struct { 256 targetable 257 Module ModuleInstance 258 Resource ResourceInstance 259 } 260 261 // ResourceInstance returns the address of a particular resource instance within the receiver. 262 func (m ModuleInstance) ResourceInstance(mode ResourceMode, typeName string, name string, key InstanceKey) AbsResourceInstance { 263 return AbsResourceInstance{ 264 Module: m, 265 Resource: ResourceInstance{ 266 Resource: Resource{ 267 Mode: mode, 268 Type: typeName, 269 Name: name, 270 }, 271 Key: key, 272 }, 273 } 274 } 275 276 // ContainingResource returns the address of the resource that contains the 277 // receving resource instance. In other words, it discards the key portion 278 // of the address to produce an AbsResource value. 279 func (r AbsResourceInstance) ContainingResource() AbsResource { 280 return AbsResource{ 281 Module: r.Module, 282 Resource: r.Resource.ContainingResource(), 283 } 284 } 285 286 // ConfigResource returns the address of the configuration block that declared 287 // this instance. 288 func (r AbsResourceInstance) ConfigResource() ConfigResource { 289 return ConfigResource{ 290 Module: r.Module.Module(), 291 Resource: r.Resource.Resource, 292 } 293 } 294 295 // TargetContains implements Targetable by returning true if the given other 296 // address is equal to the receiver. 297 func (r AbsResourceInstance) TargetContains(other Targetable) bool { 298 switch to := other.(type) { 299 300 // while we currently don't start with an AbsResourceInstance as a target 301 // address, check all resource types for consistency. 302 case AbsResourceInstance: 303 // We'll use our stringification as a cheat-ish way to test for equality. 304 return to.String() == r.String() 305 case ConfigResource: 306 return to.String() == r.String() 307 case AbsResource: 308 return to.String() == r.String() 309 310 default: 311 return false 312 313 } 314 } 315 316 func (r AbsResourceInstance) AddrType() TargetableAddrType { 317 return AbsResourceInstanceAddrType 318 } 319 320 func (r AbsResourceInstance) String() string { 321 if len(r.Module) == 0 { 322 return r.Resource.String() 323 } 324 return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String()) 325 } 326 327 // AffectedAbsResource returns the AbsResource for the instance. 328 func (r AbsResourceInstance) AffectedAbsResource() AbsResource { 329 return AbsResource{ 330 Module: r.Module, 331 Resource: r.Resource.Resource, 332 } 333 } 334 335 func (r AbsResourceInstance) CheckRule(t CheckRuleType, i int) CheckRule { 336 return CheckRule{ 337 Container: r, 338 Type: t, 339 Index: i, 340 } 341 } 342 343 func (v AbsResourceInstance) CheckableKind() CheckableKind { 344 return CheckableResource 345 } 346 347 func (r AbsResourceInstance) Equal(o AbsResourceInstance) bool { 348 return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource) 349 } 350 351 // Less returns true if the receiver should sort before the given other value 352 // in a sorted list of addresses. 353 func (r AbsResourceInstance) Less(o AbsResourceInstance) bool { 354 if !r.Module.Equal(o.Module) { 355 return r.Module.Less(o.Module) 356 } 357 358 if !r.Resource.Equal(o.Resource) { 359 return r.Resource.Less(o.Resource) 360 } 361 362 return false 363 } 364 365 // AbsResourceInstance is a Checkable 366 func (r AbsResourceInstance) checkableSigil() {} 367 368 func (r AbsResourceInstance) ConfigCheckable() ConfigCheckable { 369 // The ConfigCheckable for an AbsResourceInstance is its ConfigResource. 370 return r.ConfigResource() 371 } 372 373 type absResourceInstanceKey string 374 375 func (r AbsResourceInstance) UniqueKey() UniqueKey { 376 return absResourceInstanceKey(r.String()) 377 } 378 379 func (r absResourceInstanceKey) uniqueKeySigil() {} 380 381 func (r AbsResourceInstance) absMoveableSigil() { 382 // AbsResourceInstance is moveable 383 } 384 385 // ConfigResource is an address for a resource within a configuration. 386 type ConfigResource struct { 387 targetable 388 Module Module 389 Resource Resource 390 } 391 392 // Resource returns the address of a particular resource within the module. 393 func (m Module) Resource(mode ResourceMode, typeName string, name string) ConfigResource { 394 return ConfigResource{ 395 Module: m, 396 Resource: Resource{ 397 Mode: mode, 398 Type: typeName, 399 Name: name, 400 }, 401 } 402 } 403 404 // Absolute produces the address for the receiver within a specific module instance. 405 func (r ConfigResource) Absolute(module ModuleInstance) AbsResource { 406 return AbsResource{ 407 Module: module, 408 Resource: r.Resource, 409 } 410 } 411 412 // TargetContains implements Targetable by returning true if the given other 413 // address is either equal to the receiver or is an instance of the 414 // receiver. 415 func (r ConfigResource) TargetContains(other Targetable) bool { 416 switch to := other.(type) { 417 case ConfigResource: 418 // We'll use our stringification as a cheat-ish way to test for equality. 419 return to.String() == r.String() 420 case AbsResource: 421 return r.TargetContains(to.Config()) 422 case AbsResourceInstance: 423 return r.TargetContains(to.ContainingResource()) 424 default: 425 return false 426 } 427 } 428 429 func (r ConfigResource) AddrType() TargetableAddrType { 430 return ConfigResourceAddrType 431 } 432 433 func (r ConfigResource) String() string { 434 if len(r.Module) == 0 { 435 return r.Resource.String() 436 } 437 return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String()) 438 } 439 440 func (r ConfigResource) Equal(o ConfigResource) bool { 441 return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource) 442 } 443 444 func (r ConfigResource) UniqueKey() UniqueKey { 445 return configResourceKey(r.String()) 446 } 447 448 func (r ConfigResource) configMoveableSigil() { 449 // ConfigResource is moveable 450 } 451 452 func (r ConfigResource) configCheckableSigil() { 453 // ConfigResource represents a configuration object that declares checkable objects 454 } 455 456 func (v ConfigResource) CheckableKind() CheckableKind { 457 return CheckableResource 458 } 459 460 type configResourceKey string 461 462 func (k configResourceKey) uniqueKeySigil() {} 463 464 // ResourceMode defines which lifecycle applies to a given resource. Each 465 // resource lifecycle has a slightly different address format. 466 type ResourceMode rune 467 468 //go:generate go run golang.org/x/tools/cmd/stringer -type ResourceMode 469 470 const ( 471 // InvalidResourceMode is the zero value of ResourceMode and is not 472 // a valid resource mode. 473 InvalidResourceMode ResourceMode = 0 474 475 // ManagedResourceMode indicates a managed resource, as defined by 476 // "resource" blocks in configuration. 477 ManagedResourceMode ResourceMode = 'M' 478 479 // DataResourceMode indicates a data resource, as defined by 480 // "data" blocks in configuration. 481 DataResourceMode ResourceMode = 'D' 482 )