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