github.com/richardmarshall/terraform@v0.9.5-0.20170429023105-15704cc6ee35/helper/schema/field_reader_config_test.go (about) 1 package schema 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 "testing" 8 9 "github.com/hashicorp/hil/ast" 10 "github.com/hashicorp/terraform/config" 11 "github.com/hashicorp/terraform/helper/hashcode" 12 "github.com/hashicorp/terraform/terraform" 13 ) 14 15 func TestConfigFieldReader_impl(t *testing.T) { 16 var _ FieldReader = new(ConfigFieldReader) 17 } 18 19 func TestConfigFieldReader(t *testing.T) { 20 testFieldReader(t, func(s map[string]*Schema) FieldReader { 21 return &ConfigFieldReader{ 22 Schema: s, 23 24 Config: testConfig(t, map[string]interface{}{ 25 "bool": true, 26 "float": 3.1415, 27 "int": 42, 28 "string": "string", 29 30 "list": []interface{}{"foo", "bar"}, 31 32 "listInt": []interface{}{21, 42}, 33 34 "map": map[string]interface{}{ 35 "foo": "bar", 36 "bar": "baz", 37 }, 38 "mapInt": map[string]interface{}{ 39 "one": "1", 40 "two": "2", 41 }, 42 "mapFloat": map[string]interface{}{ 43 "oneDotTwo": "1.2", 44 }, 45 "mapBool": map[string]interface{}{ 46 "True": "true", 47 "False": "false", 48 }, 49 50 "set": []interface{}{10, 50}, 51 "setDeep": []interface{}{ 52 map[string]interface{}{ 53 "index": 10, 54 "value": "foo", 55 }, 56 map[string]interface{}{ 57 "index": 50, 58 "value": "bar", 59 }, 60 }, 61 }), 62 } 63 }) 64 } 65 66 // This contains custom table tests for our ConfigFieldReader 67 func TestConfigFieldReader_custom(t *testing.T) { 68 schema := map[string]*Schema{ 69 "bool": &Schema{ 70 Type: TypeBool, 71 }, 72 } 73 74 cases := map[string]struct { 75 Addr []string 76 Result FieldReadResult 77 Config *terraform.ResourceConfig 78 Err bool 79 }{ 80 "basic": { 81 []string{"bool"}, 82 FieldReadResult{ 83 Value: true, 84 Exists: true, 85 }, 86 testConfig(t, map[string]interface{}{ 87 "bool": true, 88 }), 89 false, 90 }, 91 92 "computed": { 93 []string{"bool"}, 94 FieldReadResult{ 95 Exists: true, 96 Computed: true, 97 }, 98 testConfigInterpolate(t, map[string]interface{}{ 99 "bool": "${var.foo}", 100 }, map[string]ast.Variable{ 101 "var.foo": ast.Variable{ 102 Value: config.UnknownVariableValue, 103 Type: ast.TypeString, 104 }, 105 }), 106 false, 107 }, 108 } 109 110 for name, tc := range cases { 111 t.Run(name, func(t *testing.T) { 112 r := &ConfigFieldReader{ 113 Schema: schema, 114 Config: tc.Config, 115 } 116 out, err := r.ReadField(tc.Addr) 117 if err != nil != tc.Err { 118 t.Fatalf("%s: err: %s", name, err) 119 } 120 if s, ok := out.Value.(*Set); ok { 121 // If it is a set, convert to a list so its more easily checked. 122 out.Value = s.List() 123 } 124 if !reflect.DeepEqual(tc.Result, out) { 125 t.Fatalf("%s: bad: %#v", name, out) 126 } 127 }) 128 } 129 } 130 131 func TestConfigFieldReader_DefaultHandling(t *testing.T) { 132 schema := map[string]*Schema{ 133 "strWithDefault": &Schema{ 134 Type: TypeString, 135 Default: "ImADefault", 136 }, 137 "strWithDefaultFunc": &Schema{ 138 Type: TypeString, 139 DefaultFunc: func() (interface{}, error) { 140 return "FuncDefault", nil 141 }, 142 }, 143 } 144 145 cases := map[string]struct { 146 Addr []string 147 Result FieldReadResult 148 Config *terraform.ResourceConfig 149 Err bool 150 }{ 151 "gets default value when no config set": { 152 []string{"strWithDefault"}, 153 FieldReadResult{ 154 Value: "ImADefault", 155 Exists: true, 156 Computed: false, 157 }, 158 testConfig(t, map[string]interface{}{}), 159 false, 160 }, 161 "config overrides default value": { 162 []string{"strWithDefault"}, 163 FieldReadResult{ 164 Value: "fromConfig", 165 Exists: true, 166 Computed: false, 167 }, 168 testConfig(t, map[string]interface{}{ 169 "strWithDefault": "fromConfig", 170 }), 171 false, 172 }, 173 "gets default from function when no config set": { 174 []string{"strWithDefaultFunc"}, 175 FieldReadResult{ 176 Value: "FuncDefault", 177 Exists: true, 178 Computed: false, 179 }, 180 testConfig(t, map[string]interface{}{}), 181 false, 182 }, 183 "config overrides default function": { 184 []string{"strWithDefaultFunc"}, 185 FieldReadResult{ 186 Value: "fromConfig", 187 Exists: true, 188 Computed: false, 189 }, 190 testConfig(t, map[string]interface{}{ 191 "strWithDefaultFunc": "fromConfig", 192 }), 193 false, 194 }, 195 } 196 197 for name, tc := range cases { 198 r := &ConfigFieldReader{ 199 Schema: schema, 200 Config: tc.Config, 201 } 202 out, err := r.ReadField(tc.Addr) 203 if err != nil != tc.Err { 204 t.Fatalf("%s: err: %s", name, err) 205 } 206 if s, ok := out.Value.(*Set); ok { 207 // If it is a set, convert to a list so its more easily checked. 208 out.Value = s.List() 209 } 210 if !reflect.DeepEqual(tc.Result, out) { 211 t.Fatalf("%s: bad: %#v", name, out) 212 } 213 } 214 } 215 216 func TestConfigFieldReader_ComputedMap(t *testing.T) { 217 schema := map[string]*Schema{ 218 "map": &Schema{ 219 Type: TypeMap, 220 Computed: true, 221 }, 222 "listmap": &Schema{ 223 Type: TypeMap, 224 Computed: true, 225 Elem: TypeList, 226 }, 227 "maplist": &Schema{ 228 Type: TypeList, 229 Computed: true, 230 Elem: TypeMap, 231 }, 232 } 233 234 cases := []struct { 235 Name string 236 Addr []string 237 Result FieldReadResult 238 Config *terraform.ResourceConfig 239 Err bool 240 }{ 241 { 242 "set, normal", 243 []string{"map"}, 244 FieldReadResult{ 245 Value: map[string]interface{}{ 246 "foo": "bar", 247 }, 248 Exists: true, 249 Computed: false, 250 }, 251 testConfig(t, map[string]interface{}{ 252 "map": map[string]interface{}{ 253 "foo": "bar", 254 }, 255 }), 256 false, 257 }, 258 259 { 260 "computed element", 261 []string{"map"}, 262 FieldReadResult{ 263 Exists: true, 264 Computed: true, 265 }, 266 testConfigInterpolate(t, map[string]interface{}{ 267 "map": map[string]interface{}{ 268 "foo": "${var.foo}", 269 }, 270 }, map[string]ast.Variable{ 271 "var.foo": ast.Variable{ 272 Value: config.UnknownVariableValue, 273 Type: ast.TypeString, 274 }, 275 }), 276 false, 277 }, 278 279 { 280 "native map", 281 []string{"map"}, 282 FieldReadResult{ 283 Value: map[string]interface{}{ 284 "bar": "baz", 285 "baz": "bar", 286 }, 287 Exists: true, 288 Computed: false, 289 }, 290 testConfigInterpolate(t, map[string]interface{}{ 291 "map": "${var.foo}", 292 }, map[string]ast.Variable{ 293 "var.foo": ast.Variable{ 294 Type: ast.TypeMap, 295 Value: map[string]ast.Variable{ 296 "bar": ast.Variable{ 297 Type: ast.TypeString, 298 Value: "baz", 299 }, 300 "baz": ast.Variable{ 301 Type: ast.TypeString, 302 Value: "bar", 303 }, 304 }, 305 }, 306 }), 307 false, 308 }, 309 310 { 311 "map-from-list-of-maps", 312 []string{"maplist", "0"}, 313 FieldReadResult{ 314 Value: map[string]interface{}{ 315 "key": "bar", 316 }, 317 Exists: true, 318 Computed: false, 319 }, 320 testConfigInterpolate(t, map[string]interface{}{ 321 "maplist": "${var.foo}", 322 }, map[string]ast.Variable{ 323 "var.foo": ast.Variable{ 324 Type: ast.TypeList, 325 Value: []ast.Variable{ 326 { 327 Type: ast.TypeMap, 328 Value: map[string]ast.Variable{ 329 "key": ast.Variable{ 330 Type: ast.TypeString, 331 Value: "bar", 332 }, 333 }, 334 }, 335 }, 336 }, 337 }), 338 false, 339 }, 340 341 { 342 "value-from-list-of-maps", 343 []string{"maplist", "0", "key"}, 344 FieldReadResult{ 345 Value: "bar", 346 Exists: true, 347 Computed: false, 348 }, 349 testConfigInterpolate(t, map[string]interface{}{ 350 "maplist": "${var.foo}", 351 }, map[string]ast.Variable{ 352 "var.foo": ast.Variable{ 353 Type: ast.TypeList, 354 Value: []ast.Variable{ 355 { 356 Type: ast.TypeMap, 357 Value: map[string]ast.Variable{ 358 "key": ast.Variable{ 359 Type: ast.TypeString, 360 Value: "bar", 361 }, 362 }, 363 }, 364 }, 365 }, 366 }), 367 false, 368 }, 369 370 { 371 "list-from-map-of-lists", 372 []string{"listmap", "key"}, 373 FieldReadResult{ 374 Value: []interface{}{"bar"}, 375 Exists: true, 376 Computed: false, 377 }, 378 testConfigInterpolate(t, map[string]interface{}{ 379 "listmap": "${var.foo}", 380 }, map[string]ast.Variable{ 381 "var.foo": ast.Variable{ 382 Type: ast.TypeMap, 383 Value: map[string]ast.Variable{ 384 "key": ast.Variable{ 385 Type: ast.TypeList, 386 Value: []ast.Variable{ 387 ast.Variable{ 388 Type: ast.TypeString, 389 Value: "bar", 390 }, 391 }, 392 }, 393 }, 394 }, 395 }), 396 false, 397 }, 398 399 { 400 "value-from-map-of-lists", 401 []string{"listmap", "key", "0"}, 402 FieldReadResult{ 403 Value: "bar", 404 Exists: true, 405 Computed: false, 406 }, 407 testConfigInterpolate(t, map[string]interface{}{ 408 "listmap": "${var.foo}", 409 }, map[string]ast.Variable{ 410 "var.foo": ast.Variable{ 411 Type: ast.TypeMap, 412 Value: map[string]ast.Variable{ 413 "key": ast.Variable{ 414 Type: ast.TypeList, 415 Value: []ast.Variable{ 416 ast.Variable{ 417 Type: ast.TypeString, 418 Value: "bar", 419 }, 420 }, 421 }, 422 }, 423 }, 424 }), 425 false, 426 }, 427 } 428 429 for i, tc := range cases { 430 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 431 r := &ConfigFieldReader{ 432 Schema: schema, 433 Config: tc.Config, 434 } 435 out, err := r.ReadField(tc.Addr) 436 if err != nil != tc.Err { 437 t.Fatal(err) 438 } 439 if s, ok := out.Value.(*Set); ok { 440 // If it is a set, convert to the raw map 441 out.Value = s.m 442 if len(s.m) == 0 { 443 out.Value = nil 444 } 445 } 446 if !reflect.DeepEqual(tc.Result, out) { 447 t.Fatalf("\nexpected: %#v\ngot: %#v", tc.Result, out) 448 } 449 }) 450 } 451 } 452 453 func TestConfigFieldReader_ComputedSet(t *testing.T) { 454 schema := map[string]*Schema{ 455 "strSet": &Schema{ 456 Type: TypeSet, 457 Elem: &Schema{Type: TypeString}, 458 Set: HashString, 459 }, 460 } 461 462 cases := map[string]struct { 463 Addr []string 464 Result FieldReadResult 465 Config *terraform.ResourceConfig 466 Err bool 467 }{ 468 "set, normal": { 469 []string{"strSet"}, 470 FieldReadResult{ 471 Value: map[string]interface{}{ 472 "2356372769": "foo", 473 }, 474 Exists: true, 475 Computed: false, 476 }, 477 testConfig(t, map[string]interface{}{ 478 "strSet": []interface{}{"foo"}, 479 }), 480 false, 481 }, 482 483 "set, computed element": { 484 []string{"strSet"}, 485 FieldReadResult{ 486 Value: nil, 487 Exists: true, 488 Computed: true, 489 }, 490 testConfigInterpolate(t, map[string]interface{}{ 491 "strSet": []interface{}{"${var.foo}"}, 492 }, map[string]ast.Variable{ 493 "var.foo": ast.Variable{ 494 Value: config.UnknownVariableValue, 495 Type: ast.TypeUnknown, 496 }, 497 }), 498 false, 499 }, 500 501 "set, computed element substring": { 502 []string{"strSet"}, 503 FieldReadResult{ 504 Value: nil, 505 Exists: true, 506 Computed: true, 507 }, 508 testConfigInterpolate(t, map[string]interface{}{ 509 "strSet": []interface{}{"${var.foo}/32"}, 510 }, map[string]ast.Variable{ 511 "var.foo": ast.Variable{ 512 Value: config.UnknownVariableValue, 513 Type: ast.TypeUnknown, 514 }, 515 }), 516 false, 517 }, 518 } 519 520 for name, tc := range cases { 521 r := &ConfigFieldReader{ 522 Schema: schema, 523 Config: tc.Config, 524 } 525 out, err := r.ReadField(tc.Addr) 526 if err != nil != tc.Err { 527 t.Fatalf("%s: err: %s", name, err) 528 } 529 if s, ok := out.Value.(*Set); ok { 530 // If it is a set, convert to the raw map 531 out.Value = s.m 532 if len(s.m) == 0 { 533 out.Value = nil 534 } 535 } 536 if !reflect.DeepEqual(tc.Result, out) { 537 t.Fatalf("%s: bad: %#v", name, out) 538 } 539 } 540 } 541 542 func TestConfigFieldReader_computedComplexSet(t *testing.T) { 543 hashfunc := func(v interface{}) int { 544 var buf bytes.Buffer 545 m := v.(map[string]interface{}) 546 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 547 buf.WriteString(fmt.Sprintf("%s-", m["vhd_uri"].(string))) 548 return hashcode.String(buf.String()) 549 } 550 551 schema := map[string]*Schema{ 552 "set": &Schema{ 553 Type: TypeSet, 554 Elem: &Resource{ 555 Schema: map[string]*Schema{ 556 "name": { 557 Type: TypeString, 558 Required: true, 559 }, 560 561 "vhd_uri": { 562 Type: TypeString, 563 Required: true, 564 }, 565 }, 566 }, 567 Set: hashfunc, 568 }, 569 } 570 571 cases := map[string]struct { 572 Addr []string 573 Result FieldReadResult 574 Config *terraform.ResourceConfig 575 Err bool 576 }{ 577 "set, normal": { 578 []string{"set"}, 579 FieldReadResult{ 580 Value: map[string]interface{}{ 581 "532860136": map[string]interface{}{ 582 "name": "myosdisk1", 583 "vhd_uri": "bar", 584 }, 585 }, 586 Exists: true, 587 Computed: false, 588 }, 589 testConfig(t, map[string]interface{}{ 590 "set": []interface{}{ 591 map[string]interface{}{ 592 "name": "myosdisk1", 593 "vhd_uri": "bar", 594 }, 595 }, 596 }), 597 false, 598 }, 599 600 "set, computed element": { 601 []string{"set"}, 602 FieldReadResult{ 603 Value: map[string]interface{}{ 604 "~3596295623": map[string]interface{}{ 605 "name": "myosdisk1", 606 "vhd_uri": "${var.foo}/bar", 607 }, 608 }, 609 Exists: true, 610 Computed: false, 611 }, 612 testConfigInterpolate(t, map[string]interface{}{ 613 "set": []interface{}{ 614 map[string]interface{}{ 615 "name": "myosdisk1", 616 "vhd_uri": "${var.foo}/bar", 617 }, 618 }, 619 }, map[string]ast.Variable{ 620 "var.foo": ast.Variable{ 621 Value: config.UnknownVariableValue, 622 Type: ast.TypeUnknown, 623 }, 624 }), 625 false, 626 }, 627 628 "set, computed element single": { 629 []string{"set", "~3596295623", "vhd_uri"}, 630 FieldReadResult{ 631 Value: "${var.foo}/bar", 632 Exists: true, 633 Computed: true, 634 }, 635 testConfigInterpolate(t, map[string]interface{}{ 636 "set": []interface{}{ 637 map[string]interface{}{ 638 "name": "myosdisk1", 639 "vhd_uri": "${var.foo}/bar", 640 }, 641 }, 642 }, map[string]ast.Variable{ 643 "var.foo": ast.Variable{ 644 Value: config.UnknownVariableValue, 645 Type: ast.TypeUnknown, 646 }, 647 }), 648 false, 649 }, 650 } 651 652 for name, tc := range cases { 653 r := &ConfigFieldReader{ 654 Schema: schema, 655 Config: tc.Config, 656 } 657 out, err := r.ReadField(tc.Addr) 658 if err != nil != tc.Err { 659 t.Fatalf("%s: err: %s", name, err) 660 } 661 if s, ok := out.Value.(*Set); ok { 662 // If it is a set, convert to the raw map 663 out.Value = s.m 664 if len(s.m) == 0 { 665 out.Value = nil 666 } 667 } 668 if !reflect.DeepEqual(tc.Result, out) { 669 t.Fatalf("%s: bad: %#v", name, out) 670 } 671 } 672 } 673 674 func testConfig( 675 t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig { 676 return testConfigInterpolate(t, raw, nil) 677 } 678 679 func testConfigInterpolate( 680 t *testing.T, 681 raw map[string]interface{}, 682 vs map[string]ast.Variable) *terraform.ResourceConfig { 683 684 rc, err := config.NewRawConfig(raw) 685 if err != nil { 686 t.Fatalf("err: %s", err) 687 } 688 if len(vs) > 0 { 689 if err := rc.Interpolate(vs); err != nil { 690 t.Fatalf("err: %s", err) 691 } 692 } 693 694 return terraform.NewResourceConfig(rc) 695 }