github.com/bpineau/terraform@v0.8.0-rc1.0.20161126184705-a8886012d185/terraform/resource_test.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 8 "github.com/hashicorp/hil" 9 "github.com/hashicorp/hil/ast" 10 "github.com/hashicorp/terraform/config" 11 "github.com/mitchellh/reflectwalk" 12 ) 13 14 func TestInstanceInfo(t *testing.T) { 15 cases := []struct { 16 Info *InstanceInfo 17 Result string 18 }{ 19 { 20 &InstanceInfo{ 21 Id: "foo", 22 }, 23 "foo", 24 }, 25 { 26 &InstanceInfo{ 27 Id: "foo", 28 ModulePath: rootModulePath, 29 }, 30 "foo", 31 }, 32 { 33 &InstanceInfo{ 34 Id: "foo", 35 ModulePath: []string{"root", "consul"}, 36 }, 37 "module.consul.foo", 38 }, 39 } 40 41 for i, tc := range cases { 42 actual := tc.Info.HumanId() 43 if actual != tc.Result { 44 t.Fatalf("%d: %s", i, actual) 45 } 46 } 47 } 48 49 func TestResourceConfigGet(t *testing.T) { 50 cases := []struct { 51 Config map[string]interface{} 52 Vars map[string]interface{} 53 Key string 54 Value interface{} 55 }{ 56 { 57 Config: nil, 58 Key: "foo", 59 Value: nil, 60 }, 61 62 { 63 Config: map[string]interface{}{ 64 "foo": "bar", 65 }, 66 Key: "foo", 67 Value: "bar", 68 }, 69 70 { 71 Config: map[string]interface{}{ 72 "foo": "${var.foo}", 73 }, 74 Key: "foo", 75 Value: "${var.foo}", 76 }, 77 78 { 79 Config: map[string]interface{}{ 80 "foo": "${var.foo}", 81 }, 82 Vars: map[string]interface{}{"foo": unknownValue()}, 83 Key: "foo", 84 Value: "${var.foo}", 85 }, 86 87 { 88 Config: map[string]interface{}{ 89 "foo": []interface{}{1, 2, 5}, 90 }, 91 Key: "foo.0", 92 Value: 1, 93 }, 94 95 { 96 Config: map[string]interface{}{ 97 "foo": []interface{}{1, 2, 5}, 98 }, 99 Key: "foo.5", 100 Value: nil, 101 }, 102 103 // get from map 104 { 105 Config: map[string]interface{}{ 106 "mapname": []map[string]interface{}{ 107 map[string]interface{}{"key": 1}, 108 }, 109 }, 110 Key: "mapname.0.key", 111 Value: 1, 112 }, 113 114 // get from map with dot in key 115 { 116 Config: map[string]interface{}{ 117 "mapname": []map[string]interface{}{ 118 map[string]interface{}{"key.name": 1}, 119 }, 120 }, 121 Key: "mapname.0.key.name", 122 Value: 1, 123 }, 124 125 // get from map with overlapping key names 126 { 127 Config: map[string]interface{}{ 128 "mapname": []map[string]interface{}{ 129 map[string]interface{}{ 130 "key.name": 1, 131 "key.name.2": 2, 132 }, 133 }, 134 }, 135 Key: "mapname.0.key.name.2", 136 Value: 2, 137 }, 138 { 139 Config: map[string]interface{}{ 140 "mapname": []map[string]interface{}{ 141 map[string]interface{}{ 142 "key.name": 1, 143 "key.name.foo": 2, 144 }, 145 }, 146 }, 147 Key: "mapname.0.key.name", 148 Value: 1, 149 }, 150 { 151 Config: map[string]interface{}{ 152 "mapname": []map[string]interface{}{ 153 map[string]interface{}{ 154 "listkey": []map[string]interface{}{ 155 {"key": 3}, 156 }, 157 }, 158 }, 159 }, 160 Key: "mapname.0.listkey.0.key", 161 Value: 3, 162 }, 163 164 // A map assigned to a list via interpolation should Get a non-existent 165 // value. The test code now also checks that Get doesn't return (nil, 166 // true), which it previously did for this configuration. 167 { 168 Config: map[string]interface{}{ 169 "maplist": "${var.maplist}", 170 }, 171 Key: "maplist.0", 172 Value: nil, 173 }, 174 175 // FIXME: this is ambiguous, and matches the nested map 176 // leaving here to catch this behaviour if it changes. 177 { 178 Config: map[string]interface{}{ 179 "mapname": []map[string]interface{}{ 180 map[string]interface{}{ 181 "key.name": 1, 182 "key.name.0": 2, 183 "key": map[string]interface{}{"name": 3}, 184 }, 185 }, 186 }, 187 Key: "mapname.0.key.name", 188 Value: 3, 189 }, 190 /* 191 // TODO: can't access this nested list at all. 192 // FIXME: key with name matching substring of nested list can panic 193 { 194 Config: map[string]interface{}{ 195 "mapname": []map[string]interface{}{ 196 map[string]interface{}{ 197 "key.name": []map[string]interface{}{ 198 {"subkey": 1}, 199 }, 200 "key": 3, 201 }, 202 }, 203 }, 204 Key: "mapname.0.key.name.0.subkey", 205 Value: 3, 206 }, 207 */ 208 } 209 210 for i, tc := range cases { 211 var rawC *config.RawConfig 212 if tc.Config != nil { 213 var err error 214 rawC, err = config.NewRawConfig(tc.Config) 215 if err != nil { 216 t.Fatalf("err: %s", err) 217 } 218 } 219 220 if tc.Vars != nil { 221 vs := make(map[string]ast.Variable) 222 for k, v := range tc.Vars { 223 hilVar, err := hil.InterfaceToVariable(v) 224 if err != nil { 225 t.Fatalf("%#v to var: %s", v, err) 226 } 227 228 vs["var."+k] = hilVar 229 } 230 231 if err := rawC.Interpolate(vs); err != nil { 232 t.Fatalf("err: %s", err) 233 } 234 } 235 236 rc := NewResourceConfig(rawC) 237 rc.interpolateForce() 238 239 // Test getting a key 240 t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) { 241 v, ok := rc.Get(tc.Key) 242 if ok && v == nil { 243 t.Fatal("(nil, true) returned from Get") 244 } 245 246 if !reflect.DeepEqual(v, tc.Value) { 247 t.Fatalf("%d bad: %#v", i, v) 248 } 249 }) 250 251 // If we have vars, we don't test copying 252 if len(tc.Vars) > 0 { 253 continue 254 } 255 256 // Test copying and equality 257 t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) { 258 copy := rc.DeepCopy() 259 if !reflect.DeepEqual(copy, rc) { 260 t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc) 261 } 262 263 if !copy.Equal(rc) { 264 t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc) 265 } 266 if !rc.Equal(copy) { 267 t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc) 268 } 269 }) 270 } 271 } 272 273 func TestResourceConfigIsComputed(t *testing.T) { 274 cases := []struct { 275 Name string 276 Config map[string]interface{} 277 Vars map[string]interface{} 278 Key string 279 Result bool 280 }{ 281 { 282 Name: "basic value", 283 Config: map[string]interface{}{ 284 "foo": "${var.foo}", 285 }, 286 Vars: map[string]interface{}{ 287 "foo": unknownValue(), 288 }, 289 Key: "foo", 290 Result: true, 291 }, 292 293 { 294 Name: "set with a computed element", 295 Config: map[string]interface{}{ 296 "foo": "${var.foo}", 297 }, 298 Vars: map[string]interface{}{ 299 "foo": []string{ 300 "a", 301 unknownValue(), 302 }, 303 }, 304 Key: "foo", 305 Result: true, 306 }, 307 308 { 309 Name: "set with no computed elements", 310 Config: map[string]interface{}{ 311 "foo": "${var.foo}", 312 }, 313 Vars: map[string]interface{}{ 314 "foo": []string{ 315 "a", 316 "b", 317 }, 318 }, 319 Key: "foo", 320 Result: false, 321 }, 322 323 /* 324 { 325 Name: "set count with computed elements", 326 Config: map[string]interface{}{ 327 "foo": "${var.foo}", 328 }, 329 Vars: map[string]interface{}{ 330 "foo": []string{ 331 "a", 332 unknownValue(), 333 }, 334 }, 335 Key: "foo.#", 336 Result: true, 337 }, 338 */ 339 340 { 341 Name: "set count with computed elements", 342 Config: map[string]interface{}{ 343 "foo": []interface{}{"${var.foo}"}, 344 }, 345 Vars: map[string]interface{}{ 346 "foo": []string{ 347 "a", 348 unknownValue(), 349 }, 350 }, 351 Key: "foo.#", 352 Result: true, 353 }, 354 355 { 356 Name: "nested set with computed elements", 357 Config: map[string]interface{}{ 358 "route": []map[string]interface{}{ 359 map[string]interface{}{ 360 "index": "1", 361 "gateway": []interface{}{"${var.foo}"}, 362 }, 363 }, 364 }, 365 Vars: map[string]interface{}{ 366 "foo": unknownValue(), 367 }, 368 Key: "route.0.gateway", 369 Result: true, 370 }, 371 } 372 373 for i, tc := range cases { 374 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 375 var rawC *config.RawConfig 376 if tc.Config != nil { 377 var err error 378 rawC, err = config.NewRawConfig(tc.Config) 379 if err != nil { 380 t.Fatalf("err: %s", err) 381 } 382 } 383 384 if tc.Vars != nil { 385 vs := make(map[string]ast.Variable) 386 for k, v := range tc.Vars { 387 hilVar, err := hil.InterfaceToVariable(v) 388 if err != nil { 389 t.Fatalf("%#v to var: %s", v, err) 390 } 391 392 vs["var."+k] = hilVar 393 } 394 395 if err := rawC.Interpolate(vs); err != nil { 396 t.Fatalf("err: %s", err) 397 } 398 } 399 400 rc := NewResourceConfig(rawC) 401 rc.interpolateForce() 402 403 t.Logf("Config: %#v", rc) 404 405 actual := rc.IsComputed(tc.Key) 406 if actual != tc.Result { 407 t.Fatalf("bad: %#v", actual) 408 } 409 }) 410 } 411 } 412 413 func TestResourceConfigCheckSet(t *testing.T) { 414 cases := []struct { 415 Name string 416 Config map[string]interface{} 417 Vars map[string]interface{} 418 Input []string 419 Errs bool 420 }{ 421 { 422 Name: "computed basic", 423 Config: map[string]interface{}{ 424 "foo": "${var.foo}", 425 }, 426 Vars: map[string]interface{}{ 427 "foo": unknownValue(), 428 }, 429 Input: []string{"foo"}, 430 Errs: false, 431 }, 432 433 { 434 Name: "basic", 435 Config: map[string]interface{}{ 436 "foo": "bar", 437 }, 438 Vars: nil, 439 Input: []string{"foo"}, 440 Errs: false, 441 }, 442 443 { 444 Name: "basic with not set", 445 Config: map[string]interface{}{ 446 "foo": "bar", 447 }, 448 Vars: nil, 449 Input: []string{"foo", "bar"}, 450 Errs: true, 451 }, 452 453 { 454 Name: "basic with one computed", 455 Config: map[string]interface{}{ 456 "foo": "bar", 457 "bar": "${var.foo}", 458 }, 459 Vars: map[string]interface{}{ 460 "foo": unknownValue(), 461 }, 462 Input: []string{"foo", "bar"}, 463 Errs: false, 464 }, 465 } 466 467 for i, tc := range cases { 468 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 469 var rawC *config.RawConfig 470 if tc.Config != nil { 471 var err error 472 rawC, err = config.NewRawConfig(tc.Config) 473 if err != nil { 474 t.Fatalf("err: %s", err) 475 } 476 } 477 478 if tc.Vars != nil { 479 vs := make(map[string]ast.Variable) 480 for k, v := range tc.Vars { 481 hilVar, err := hil.InterfaceToVariable(v) 482 if err != nil { 483 t.Fatalf("%#v to var: %s", v, err) 484 } 485 486 vs["var."+k] = hilVar 487 } 488 489 if err := rawC.Interpolate(vs); err != nil { 490 t.Fatalf("err: %s", err) 491 } 492 } 493 494 rc := NewResourceConfig(rawC) 495 rc.interpolateForce() 496 497 t.Logf("Config: %#v", rc) 498 499 errs := rc.CheckSet(tc.Input) 500 if tc.Errs != (len(errs) > 0) { 501 t.Fatalf("bad: %#v", errs) 502 } 503 }) 504 } 505 } 506 507 func TestResourceConfigDeepCopy_nil(t *testing.T) { 508 var nilRc *ResourceConfig 509 actual := nilRc.DeepCopy() 510 if actual != nil { 511 t.Fatalf("bad: %#v", actual) 512 } 513 } 514 515 func TestResourceConfigDeepCopy_nilComputed(t *testing.T) { 516 rc := &ResourceConfig{} 517 actual := rc.DeepCopy() 518 if actual.ComputedKeys != nil { 519 t.Fatalf("bad: %#v", actual) 520 } 521 } 522 523 func TestResourceConfigEqual_nil(t *testing.T) { 524 var nilRc *ResourceConfig 525 notNil := NewResourceConfig(nil) 526 527 if nilRc.Equal(notNil) { 528 t.Fatal("should not be equal") 529 } 530 531 if notNil.Equal(nilRc) { 532 t.Fatal("should not be equal") 533 } 534 } 535 536 func TestResourceConfigEqual_computedKeyOrder(t *testing.T) { 537 c := map[string]interface{}{"foo": "${a.b.c}"} 538 rc := NewResourceConfig(config.TestRawConfig(t, c)) 539 rc2 := NewResourceConfig(config.TestRawConfig(t, c)) 540 541 // Set the computed keys manual 542 rc.ComputedKeys = []string{"foo", "bar"} 543 rc2.ComputedKeys = []string{"bar", "foo"} 544 545 if !rc.Equal(rc2) { 546 t.Fatal("should be equal") 547 } 548 } 549 550 func TestUnknownCheckWalker(t *testing.T) { 551 cases := []struct { 552 Name string 553 Input interface{} 554 Result bool 555 }{ 556 { 557 "primitive", 558 42, 559 false, 560 }, 561 562 { 563 "primitive computed", 564 unknownValue(), 565 true, 566 }, 567 568 { 569 "list", 570 []interface{}{"foo", unknownValue()}, 571 true, 572 }, 573 574 { 575 "nested list", 576 []interface{}{ 577 "foo", 578 []interface{}{unknownValue()}, 579 }, 580 true, 581 }, 582 } 583 584 for i, tc := range cases { 585 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 586 var w unknownCheckWalker 587 if err := reflectwalk.Walk(tc.Input, &w); err != nil { 588 t.Fatalf("err: %s", err) 589 } 590 591 if w.Unknown != tc.Result { 592 t.Fatalf("bad: %v", w.Unknown) 593 } 594 }) 595 } 596 } 597 598 func testResourceConfig( 599 t *testing.T, c map[string]interface{}) *ResourceConfig { 600 raw, err := config.NewRawConfig(c) 601 if err != nil { 602 t.Fatalf("err: %s", err) 603 } 604 605 return NewResourceConfig(raw) 606 }