github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/terraform/transform_orphan_count_test.go (about) 1 package terraform 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/hashicorp/terraform/addrs" 8 "github.com/hashicorp/terraform/states" 9 "github.com/zclconf/go-cty/cty" 10 ) 11 12 func TestOrphanResourceCountTransformer(t *testing.T) { 13 state := MustShimLegacyState(&State{ 14 Modules: []*ModuleState{ 15 &ModuleState{ 16 Path: []string{"root"}, 17 Resources: map[string]*ResourceState{ 18 "aws_instance.web": &ResourceState{ 19 Type: "aws_instance", 20 Primary: &InstanceState{ 21 ID: "foo", 22 }, 23 }, 24 25 "aws_instance.foo": &ResourceState{ 26 Type: "aws_instance", 27 Primary: &InstanceState{ 28 ID: "foo", 29 }, 30 }, 31 32 "aws_instance.foo.2": &ResourceState{ 33 Type: "aws_instance", 34 Primary: &InstanceState{ 35 ID: "foo", 36 }, 37 }, 38 }, 39 }, 40 }, 41 }) 42 43 g := Graph{Path: addrs.RootModuleInstance} 44 45 { 46 tf := &OrphanResourceCountTransformer{ 47 Concrete: testOrphanResourceConcreteFunc, 48 Count: 1, 49 Addr: addrs.RootModuleInstance.Resource( 50 addrs.ManagedResourceMode, "aws_instance", "foo", 51 ), 52 State: state, 53 } 54 if err := tf.Transform(&g); err != nil { 55 t.Fatalf("err: %s", err) 56 } 57 } 58 59 actual := strings.TrimSpace(g.String()) 60 expected := strings.TrimSpace(testTransformOrphanResourceCountBasicStr) 61 if actual != expected { 62 t.Fatalf("bad:\n\n%s", actual) 63 } 64 } 65 66 func TestOrphanResourceCountTransformer_zero(t *testing.T) { 67 state := MustShimLegacyState(&State{ 68 Modules: []*ModuleState{ 69 &ModuleState{ 70 Path: []string{"root"}, 71 Resources: map[string]*ResourceState{ 72 "aws_instance.web": &ResourceState{ 73 Type: "aws_instance", 74 Primary: &InstanceState{ 75 ID: "foo", 76 }, 77 }, 78 79 "aws_instance.foo": &ResourceState{ 80 Type: "aws_instance", 81 Primary: &InstanceState{ 82 ID: "foo", 83 }, 84 }, 85 86 "aws_instance.foo.2": &ResourceState{ 87 Type: "aws_instance", 88 Primary: &InstanceState{ 89 ID: "foo", 90 }, 91 }, 92 }, 93 }, 94 }, 95 }) 96 97 g := Graph{Path: addrs.RootModuleInstance} 98 99 { 100 tf := &OrphanResourceCountTransformer{ 101 Concrete: testOrphanResourceConcreteFunc, 102 Count: 0, 103 Addr: addrs.RootModuleInstance.Resource( 104 addrs.ManagedResourceMode, "aws_instance", "foo", 105 ), 106 State: state, 107 } 108 if err := tf.Transform(&g); err != nil { 109 t.Fatalf("err: %s", err) 110 } 111 } 112 113 actual := strings.TrimSpace(g.String()) 114 expected := strings.TrimSpace(testTransformOrphanResourceCountZeroStr) 115 if actual != expected { 116 t.Fatalf("bad:\n\n%s", actual) 117 } 118 } 119 120 func TestOrphanResourceCountTransformer_oneNoIndex(t *testing.T) { 121 state := MustShimLegacyState(&State{ 122 Modules: []*ModuleState{ 123 &ModuleState{ 124 Path: []string{"root"}, 125 Resources: map[string]*ResourceState{ 126 "aws_instance.web": &ResourceState{ 127 Type: "aws_instance", 128 Primary: &InstanceState{ 129 ID: "foo", 130 }, 131 }, 132 133 "aws_instance.foo": &ResourceState{ 134 Type: "aws_instance", 135 Primary: &InstanceState{ 136 ID: "foo", 137 }, 138 }, 139 140 "aws_instance.foo.2": &ResourceState{ 141 Type: "aws_instance", 142 Primary: &InstanceState{ 143 ID: "foo", 144 }, 145 }, 146 }, 147 }, 148 }, 149 }) 150 151 g := Graph{Path: addrs.RootModuleInstance} 152 153 { 154 tf := &OrphanResourceCountTransformer{ 155 Concrete: testOrphanResourceConcreteFunc, 156 Count: 1, 157 Addr: addrs.RootModuleInstance.Resource( 158 addrs.ManagedResourceMode, "aws_instance", "foo", 159 ), 160 State: state, 161 } 162 if err := tf.Transform(&g); err != nil { 163 t.Fatalf("err: %s", err) 164 } 165 } 166 167 actual := strings.TrimSpace(g.String()) 168 expected := strings.TrimSpace(testTransformOrphanResourceCountOneNoIndexStr) 169 if actual != expected { 170 t.Fatalf("bad:\n\n%s", actual) 171 } 172 } 173 174 func TestOrphanResourceCountTransformer_oneIndex(t *testing.T) { 175 state := MustShimLegacyState(&State{ 176 Modules: []*ModuleState{ 177 &ModuleState{ 178 Path: []string{"root"}, 179 Resources: map[string]*ResourceState{ 180 "aws_instance.web": &ResourceState{ 181 Type: "aws_instance", 182 Primary: &InstanceState{ 183 ID: "foo", 184 }, 185 }, 186 187 "aws_instance.foo.0": &ResourceState{ 188 Type: "aws_instance", 189 Primary: &InstanceState{ 190 ID: "foo", 191 }, 192 }, 193 194 "aws_instance.foo.1": &ResourceState{ 195 Type: "aws_instance", 196 Primary: &InstanceState{ 197 ID: "foo", 198 }, 199 }, 200 }, 201 }, 202 }, 203 }) 204 205 g := Graph{Path: addrs.RootModuleInstance} 206 207 { 208 tf := &OrphanResourceCountTransformer{ 209 Concrete: testOrphanResourceConcreteFunc, 210 Count: 1, 211 Addr: addrs.RootModuleInstance.Resource( 212 addrs.ManagedResourceMode, "aws_instance", "foo", 213 ), 214 State: state, 215 } 216 if err := tf.Transform(&g); err != nil { 217 t.Fatalf("err: %s", err) 218 } 219 } 220 221 actual := strings.TrimSpace(g.String()) 222 expected := strings.TrimSpace(testTransformOrphanResourceCountOneIndexStr) 223 if actual != expected { 224 t.Fatalf("bad:\n\n%s", actual) 225 } 226 } 227 228 func TestOrphanResourceCountTransformer_zeroAndNone(t *testing.T) { 229 state := MustShimLegacyState(&State{ 230 Modules: []*ModuleState{ 231 &ModuleState{ 232 Path: []string{"root"}, 233 Resources: map[string]*ResourceState{ 234 "aws_instance.web": &ResourceState{ 235 Type: "aws_instance", 236 Primary: &InstanceState{ 237 ID: "foo", 238 }, 239 }, 240 241 "aws_instance.foo": &ResourceState{ 242 Type: "aws_instance", 243 Primary: &InstanceState{ 244 ID: "foo", 245 }, 246 }, 247 248 "aws_instance.foo.0": &ResourceState{ 249 Type: "aws_instance", 250 Primary: &InstanceState{ 251 ID: "foo", 252 }, 253 }, 254 }, 255 }, 256 }, 257 }) 258 259 g := Graph{Path: addrs.RootModuleInstance} 260 261 { 262 tf := &OrphanResourceCountTransformer{ 263 Concrete: testOrphanResourceConcreteFunc, 264 Count: -1, 265 Addr: addrs.RootModuleInstance.Resource( 266 addrs.ManagedResourceMode, "aws_instance", "foo", 267 ), 268 State: state, 269 } 270 if err := tf.Transform(&g); err != nil { 271 t.Fatalf("err: %s", err) 272 } 273 } 274 275 actual := strings.TrimSpace(g.String()) 276 expected := strings.TrimSpace(testTransformOrphanResourceCountZeroAndNoneStr) 277 if actual != expected { 278 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 279 } 280 } 281 282 func TestOrphanResourceCountTransformer_zeroAndNoneCount(t *testing.T) { 283 state := MustShimLegacyState(&State{ 284 Modules: []*ModuleState{ 285 &ModuleState{ 286 Path: []string{"root"}, 287 Resources: map[string]*ResourceState{ 288 "aws_instance.web": &ResourceState{ 289 Type: "aws_instance", 290 Primary: &InstanceState{ 291 ID: "foo", 292 }, 293 }, 294 295 "aws_instance.foo": &ResourceState{ 296 Type: "aws_instance", 297 Primary: &InstanceState{ 298 ID: "foo", 299 }, 300 }, 301 302 "aws_instance.foo.0": &ResourceState{ 303 Type: "aws_instance", 304 Primary: &InstanceState{ 305 ID: "foo", 306 }, 307 }, 308 }, 309 }, 310 }, 311 }) 312 313 g := Graph{Path: addrs.RootModuleInstance} 314 315 { 316 tf := &OrphanResourceCountTransformer{ 317 Concrete: testOrphanResourceConcreteFunc, 318 Count: 2, 319 Addr: addrs.RootModuleInstance.Resource( 320 addrs.ManagedResourceMode, "aws_instance", "foo", 321 ), 322 State: state, 323 } 324 if err := tf.Transform(&g); err != nil { 325 t.Fatalf("err: %s", err) 326 } 327 } 328 329 actual := strings.TrimSpace(g.String()) 330 expected := strings.TrimSpace(testTransformOrphanResourceCountZeroAndNoneCountStr) 331 if actual != expected { 332 t.Fatalf("bad:\n\n%s", actual) 333 } 334 } 335 336 // When converting from a NoEach mode to an EachMap via a switch to for_each, 337 // an edge is necessary to ensure that the map-key'd instances 338 // are evaluated after the NoKey resource, because the final instance evaluated 339 // sets the whole resource's EachMode. 340 func TestOrphanResourceCountTransformer_ForEachEdgesAdded(t *testing.T) { 341 state := states.BuildState(func(s *states.SyncState) { 342 // "bar" key'd resource 343 s.SetResourceInstanceCurrent( 344 addrs.Resource{ 345 Mode: addrs.ManagedResourceMode, 346 Type: "aws_instance", 347 Name: "foo", 348 }.Instance(addrs.StringKey("bar")).Absolute(addrs.RootModuleInstance), 349 &states.ResourceInstanceObjectSrc{ 350 AttrsFlat: map[string]string{ 351 "id": "foo", 352 }, 353 Status: states.ObjectReady, 354 }, 355 addrs.ProviderConfig{ 356 Type: addrs.NewLegacyProvider("aws"), 357 }.Absolute(addrs.RootModuleInstance), 358 ) 359 360 // NoKey'd resource 361 s.SetResourceInstanceCurrent( 362 addrs.Resource{ 363 Mode: addrs.ManagedResourceMode, 364 Type: "aws_instance", 365 Name: "foo", 366 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 367 &states.ResourceInstanceObjectSrc{ 368 AttrsFlat: map[string]string{ 369 "id": "foo", 370 }, 371 Status: states.ObjectReady, 372 }, 373 addrs.ProviderConfig{ 374 Type: addrs.NewLegacyProvider("aws"), 375 }.Absolute(addrs.RootModuleInstance), 376 ) 377 }) 378 379 g := Graph{Path: addrs.RootModuleInstance} 380 381 { 382 tf := &OrphanResourceCountTransformer{ 383 Concrete: testOrphanResourceConcreteFunc, 384 // No keys in this ForEach ensure both our resources end 385 // up orphaned in this test 386 ForEach: map[string]cty.Value{}, 387 Addr: addrs.RootModuleInstance.Resource( 388 addrs.ManagedResourceMode, "aws_instance", "foo", 389 ), 390 State: state, 391 } 392 if err := tf.Transform(&g); err != nil { 393 t.Fatalf("err: %s", err) 394 } 395 } 396 397 actual := strings.TrimSpace(g.String()) 398 expected := strings.TrimSpace(testTransformOrphanResourceForEachStr) 399 if actual != expected { 400 t.Fatalf("bad:\n\n%s", actual) 401 } 402 } 403 404 const testTransformOrphanResourceCountBasicStr = ` 405 aws_instance.foo[2] (orphan) 406 ` 407 408 const testTransformOrphanResourceCountZeroStr = ` 409 aws_instance.foo (orphan) 410 aws_instance.foo[2] (orphan) 411 ` 412 413 const testTransformOrphanResourceCountOneNoIndexStr = ` 414 aws_instance.foo[2] (orphan) 415 ` 416 417 const testTransformOrphanResourceCountOneIndexStr = ` 418 aws_instance.foo[1] (orphan) 419 ` 420 421 const testTransformOrphanResourceCountZeroAndNoneStr = ` 422 aws_instance.foo[0] (orphan) 423 ` 424 425 const testTransformOrphanResourceCountZeroAndNoneCountStr = ` 426 aws_instance.foo (orphan) 427 ` 428 429 const testTransformOrphanResourceForEachStr = ` 430 aws_instance.foo (orphan) 431 aws_instance.foo["bar"] (orphan) 432 aws_instance.foo (orphan) 433 `