github.com/rhenning/terraform@v0.8.0-beta2/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 // FIXME: this is ambiguous, and matches the nested map 164 // leaving here to catch this behaviour if it changes. 165 { 166 Config: map[string]interface{}{ 167 "mapname": []map[string]interface{}{ 168 map[string]interface{}{ 169 "key.name": 1, 170 "key.name.0": 2, 171 "key": map[string]interface{}{"name": 3}, 172 }, 173 }, 174 }, 175 Key: "mapname.0.key.name", 176 Value: 3, 177 }, 178 /* 179 // TODO: can't access this nested list at all. 180 // FIXME: key with name matching substring of nested list can panic 181 { 182 Config: map[string]interface{}{ 183 "mapname": []map[string]interface{}{ 184 map[string]interface{}{ 185 "key.name": []map[string]interface{}{ 186 {"subkey": 1}, 187 }, 188 "key": 3, 189 }, 190 }, 191 }, 192 Key: "mapname.0.key.name.0.subkey", 193 Value: 3, 194 }, 195 */ 196 } 197 198 for i, tc := range cases { 199 var rawC *config.RawConfig 200 if tc.Config != nil { 201 var err error 202 rawC, err = config.NewRawConfig(tc.Config) 203 if err != nil { 204 t.Fatalf("err: %s", err) 205 } 206 } 207 208 if tc.Vars != nil { 209 vs := make(map[string]ast.Variable) 210 for k, v := range tc.Vars { 211 hilVar, err := hil.InterfaceToVariable(v) 212 if err != nil { 213 t.Fatalf("%#v to var: %s", v, err) 214 } 215 216 vs["var."+k] = hilVar 217 } 218 219 if err := rawC.Interpolate(vs); err != nil { 220 t.Fatalf("err: %s", err) 221 } 222 } 223 224 rc := NewResourceConfig(rawC) 225 rc.interpolateForce() 226 227 // Test getting a key 228 t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) { 229 v, _ := rc.Get(tc.Key) 230 if !reflect.DeepEqual(v, tc.Value) { 231 t.Fatalf("%d bad: %#v", i, v) 232 } 233 }) 234 235 // If we have vars, we don't test copying 236 if len(tc.Vars) > 0 { 237 continue 238 } 239 240 // Test copying and equality 241 t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) { 242 copy := rc.DeepCopy() 243 if !reflect.DeepEqual(copy, rc) { 244 t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc) 245 } 246 247 if !copy.Equal(rc) { 248 t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc) 249 } 250 if !rc.Equal(copy) { 251 t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc) 252 } 253 }) 254 } 255 } 256 257 func TestResourceConfigIsComputed(t *testing.T) { 258 cases := []struct { 259 Name string 260 Config map[string]interface{} 261 Vars map[string]interface{} 262 Key string 263 Result bool 264 }{ 265 { 266 Name: "basic value", 267 Config: map[string]interface{}{ 268 "foo": "${var.foo}", 269 }, 270 Vars: map[string]interface{}{ 271 "foo": unknownValue(), 272 }, 273 Key: "foo", 274 Result: true, 275 }, 276 277 { 278 Name: "set with a computed element", 279 Config: map[string]interface{}{ 280 "foo": "${var.foo}", 281 }, 282 Vars: map[string]interface{}{ 283 "foo": []string{ 284 "a", 285 unknownValue(), 286 }, 287 }, 288 Key: "foo", 289 Result: true, 290 }, 291 292 { 293 Name: "set with no computed elements", 294 Config: map[string]interface{}{ 295 "foo": "${var.foo}", 296 }, 297 Vars: map[string]interface{}{ 298 "foo": []string{ 299 "a", 300 "b", 301 }, 302 }, 303 Key: "foo", 304 Result: false, 305 }, 306 307 /* 308 { 309 Name: "set count with computed elements", 310 Config: map[string]interface{}{ 311 "foo": "${var.foo}", 312 }, 313 Vars: map[string]interface{}{ 314 "foo": []string{ 315 "a", 316 unknownValue(), 317 }, 318 }, 319 Key: "foo.#", 320 Result: true, 321 }, 322 */ 323 324 { 325 Name: "set count with computed elements", 326 Config: map[string]interface{}{ 327 "foo": []interface{}{"${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 Name: "nested set with computed elements", 341 Config: map[string]interface{}{ 342 "route": []map[string]interface{}{ 343 map[string]interface{}{ 344 "index": "1", 345 "gateway": []interface{}{"${var.foo}"}, 346 }, 347 }, 348 }, 349 Vars: map[string]interface{}{ 350 "foo": unknownValue(), 351 }, 352 Key: "route.0.gateway", 353 Result: true, 354 }, 355 } 356 357 for i, tc := range cases { 358 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 359 var rawC *config.RawConfig 360 if tc.Config != nil { 361 var err error 362 rawC, err = config.NewRawConfig(tc.Config) 363 if err != nil { 364 t.Fatalf("err: %s", err) 365 } 366 } 367 368 if tc.Vars != nil { 369 vs := make(map[string]ast.Variable) 370 for k, v := range tc.Vars { 371 hilVar, err := hil.InterfaceToVariable(v) 372 if err != nil { 373 t.Fatalf("%#v to var: %s", v, err) 374 } 375 376 vs["var."+k] = hilVar 377 } 378 379 if err := rawC.Interpolate(vs); err != nil { 380 t.Fatalf("err: %s", err) 381 } 382 } 383 384 rc := NewResourceConfig(rawC) 385 rc.interpolateForce() 386 387 t.Logf("Config: %#v", rc) 388 389 actual := rc.IsComputed(tc.Key) 390 if actual != tc.Result { 391 t.Fatalf("bad: %#v", actual) 392 } 393 }) 394 } 395 } 396 397 func TestResourceConfigCheckSet(t *testing.T) { 398 cases := []struct { 399 Name string 400 Config map[string]interface{} 401 Vars map[string]interface{} 402 Input []string 403 Errs bool 404 }{ 405 { 406 Name: "computed basic", 407 Config: map[string]interface{}{ 408 "foo": "${var.foo}", 409 }, 410 Vars: map[string]interface{}{ 411 "foo": unknownValue(), 412 }, 413 Input: []string{"foo"}, 414 Errs: false, 415 }, 416 417 { 418 Name: "basic", 419 Config: map[string]interface{}{ 420 "foo": "bar", 421 }, 422 Vars: nil, 423 Input: []string{"foo"}, 424 Errs: false, 425 }, 426 427 { 428 Name: "basic with not set", 429 Config: map[string]interface{}{ 430 "foo": "bar", 431 }, 432 Vars: nil, 433 Input: []string{"foo", "bar"}, 434 Errs: true, 435 }, 436 437 { 438 Name: "basic with one computed", 439 Config: map[string]interface{}{ 440 "foo": "bar", 441 "bar": "${var.foo}", 442 }, 443 Vars: map[string]interface{}{ 444 "foo": unknownValue(), 445 }, 446 Input: []string{"foo", "bar"}, 447 Errs: false, 448 }, 449 } 450 451 for i, tc := range cases { 452 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 453 var rawC *config.RawConfig 454 if tc.Config != nil { 455 var err error 456 rawC, err = config.NewRawConfig(tc.Config) 457 if err != nil { 458 t.Fatalf("err: %s", err) 459 } 460 } 461 462 if tc.Vars != nil { 463 vs := make(map[string]ast.Variable) 464 for k, v := range tc.Vars { 465 hilVar, err := hil.InterfaceToVariable(v) 466 if err != nil { 467 t.Fatalf("%#v to var: %s", v, err) 468 } 469 470 vs["var."+k] = hilVar 471 } 472 473 if err := rawC.Interpolate(vs); err != nil { 474 t.Fatalf("err: %s", err) 475 } 476 } 477 478 rc := NewResourceConfig(rawC) 479 rc.interpolateForce() 480 481 t.Logf("Config: %#v", rc) 482 483 errs := rc.CheckSet(tc.Input) 484 if tc.Errs != (len(errs) > 0) { 485 t.Fatalf("bad: %#v", errs) 486 } 487 }) 488 } 489 } 490 491 func TestResourceConfigDeepCopy_nil(t *testing.T) { 492 var nilRc *ResourceConfig 493 actual := nilRc.DeepCopy() 494 if actual != nil { 495 t.Fatalf("bad: %#v", actual) 496 } 497 } 498 499 func TestResourceConfigDeepCopy_nilComputed(t *testing.T) { 500 rc := &ResourceConfig{} 501 actual := rc.DeepCopy() 502 if actual.ComputedKeys != nil { 503 t.Fatalf("bad: %#v", actual) 504 } 505 } 506 507 func TestResourceConfigEqual_nil(t *testing.T) { 508 var nilRc *ResourceConfig 509 notNil := NewResourceConfig(nil) 510 511 if nilRc.Equal(notNil) { 512 t.Fatal("should not be equal") 513 } 514 515 if notNil.Equal(nilRc) { 516 t.Fatal("should not be equal") 517 } 518 } 519 520 func TestResourceConfigEqual_computedKeyOrder(t *testing.T) { 521 c := map[string]interface{}{"foo": "${a.b.c}"} 522 rc := NewResourceConfig(config.TestRawConfig(t, c)) 523 rc2 := NewResourceConfig(config.TestRawConfig(t, c)) 524 525 // Set the computed keys manual 526 rc.ComputedKeys = []string{"foo", "bar"} 527 rc2.ComputedKeys = []string{"bar", "foo"} 528 529 if !rc.Equal(rc2) { 530 t.Fatal("should be equal") 531 } 532 } 533 534 func TestUnknownCheckWalker(t *testing.T) { 535 cases := []struct { 536 Name string 537 Input interface{} 538 Result bool 539 }{ 540 { 541 "primitive", 542 42, 543 false, 544 }, 545 546 { 547 "primitive computed", 548 unknownValue(), 549 true, 550 }, 551 552 { 553 "list", 554 []interface{}{"foo", unknownValue()}, 555 true, 556 }, 557 558 { 559 "nested list", 560 []interface{}{ 561 "foo", 562 []interface{}{unknownValue()}, 563 }, 564 true, 565 }, 566 } 567 568 for i, tc := range cases { 569 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 570 var w unknownCheckWalker 571 if err := reflectwalk.Walk(tc.Input, &w); err != nil { 572 t.Fatalf("err: %s", err) 573 } 574 575 if w.Unknown != tc.Result { 576 t.Fatalf("bad: %v", w.Unknown) 577 } 578 }) 579 } 580 } 581 582 func testResourceConfig( 583 t *testing.T, c map[string]interface{}) *ResourceConfig { 584 raw, err := config.NewRawConfig(c) 585 if err != nil { 586 t.Fatalf("err: %s", err) 587 } 588 589 return NewResourceConfig(raw) 590 }