github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/plans/objchange/objchange_test.go (about) 1 package objchange 2 3 import ( 4 "testing" 5 6 "github.com/apparentlymart/go-dump/dump" 7 "github.com/zclconf/go-cty/cty" 8 9 "github.com/hashicorp/terraform/internal/configs/configschema" 10 ) 11 12 func TestProposedNew(t *testing.T) { 13 tests := map[string]struct { 14 Schema *configschema.Block 15 Prior cty.Value 16 Config cty.Value 17 Want cty.Value 18 }{ 19 "empty": { 20 &configschema.Block{}, 21 cty.EmptyObjectVal, 22 cty.EmptyObjectVal, 23 cty.EmptyObjectVal, 24 }, 25 "no prior": { 26 &configschema.Block{ 27 Attributes: map[string]*configschema.Attribute{ 28 "foo": { 29 Type: cty.String, 30 Optional: true, 31 }, 32 "bar": { 33 Type: cty.String, 34 Computed: true, 35 }, 36 "bloop": { 37 NestedType: &configschema.Object{ 38 Nesting: configschema.NestingSingle, 39 Attributes: map[string]*configschema.Attribute{ 40 "blop": { 41 Type: cty.String, 42 Required: true, 43 }, 44 }, 45 }, 46 Computed: true, 47 }, 48 }, 49 BlockTypes: map[string]*configschema.NestedBlock{ 50 "baz": { 51 Nesting: configschema.NestingSingle, 52 Block: configschema.Block{ 53 Attributes: map[string]*configschema.Attribute{ 54 "boz": { 55 Type: cty.String, 56 Optional: true, 57 Computed: true, 58 }, 59 "biz": { 60 Type: cty.String, 61 Optional: true, 62 Computed: true, 63 }, 64 }, 65 }, 66 }, 67 }, 68 }, 69 cty.NullVal(cty.DynamicPseudoType), 70 cty.ObjectVal(map[string]cty.Value{ 71 "foo": cty.StringVal("hello"), 72 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 73 "blop": cty.String, 74 })), 75 "bar": cty.NullVal(cty.String), 76 "baz": cty.ObjectVal(map[string]cty.Value{ 77 "boz": cty.StringVal("world"), 78 79 // An unknown in the config represents a situation where 80 // an argument is explicitly set to an expression result 81 // that is derived from an unknown value. This is distinct 82 // from leaving it null, which allows the provider itself 83 // to decide the value during PlanResourceChange. 84 "biz": cty.UnknownVal(cty.String), 85 }), 86 }), 87 cty.ObjectVal(map[string]cty.Value{ 88 "foo": cty.StringVal("hello"), 89 90 // unset computed attributes are null in the proposal; provider 91 // usually changes them to "unknown" during PlanResourceChange, 92 // to indicate that the value will be decided during apply. 93 "bar": cty.NullVal(cty.String), 94 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 95 "blop": cty.String, 96 })), 97 98 "baz": cty.ObjectVal(map[string]cty.Value{ 99 "boz": cty.StringVal("world"), 100 "biz": cty.UnknownVal(cty.String), // explicit unknown preserved from config 101 }), 102 }), 103 }, 104 "null block remains null": { 105 &configschema.Block{ 106 Attributes: map[string]*configschema.Attribute{ 107 "foo": { 108 Type: cty.String, 109 Optional: true, 110 }, 111 "bloop": { 112 NestedType: &configschema.Object{ 113 Nesting: configschema.NestingSingle, 114 Attributes: map[string]*configschema.Attribute{ 115 "blop": { 116 Type: cty.String, 117 Required: true, 118 }, 119 }, 120 }, 121 Computed: true, 122 }, 123 }, 124 BlockTypes: map[string]*configschema.NestedBlock{ 125 "baz": { 126 Nesting: configschema.NestingSingle, 127 Block: configschema.Block{ 128 Attributes: map[string]*configschema.Attribute{ 129 "boz": { 130 Type: cty.String, 131 Optional: true, 132 Computed: true, 133 }, 134 }, 135 }, 136 }, 137 }, 138 }, 139 cty.NullVal(cty.DynamicPseudoType), 140 cty.ObjectVal(map[string]cty.Value{ 141 "foo": cty.StringVal("bar"), 142 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 143 "blop": cty.String, 144 })), 145 "baz": cty.NullVal(cty.Object(map[string]cty.Type{ 146 "boz": cty.String, 147 })), 148 }), 149 // The bloop attribue and baz block does not exist in the config, 150 // and therefore shouldn't be planned. 151 cty.ObjectVal(map[string]cty.Value{ 152 "foo": cty.StringVal("bar"), 153 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 154 "blop": cty.String, 155 })), 156 "baz": cty.NullVal(cty.Object(map[string]cty.Type{ 157 "boz": cty.String, 158 })), 159 }), 160 }, 161 "no prior with set": { 162 // This one is here because our handling of sets is more complex 163 // than others (due to the fuzzy correlation heuristic) and 164 // historically that caused us some panic-related grief. 165 &configschema.Block{ 166 BlockTypes: map[string]*configschema.NestedBlock{ 167 "baz": { 168 Nesting: configschema.NestingSet, 169 Block: configschema.Block{ 170 Attributes: map[string]*configschema.Attribute{ 171 "boz": { 172 Type: cty.String, 173 Optional: true, 174 Computed: true, 175 }, 176 }, 177 }, 178 }, 179 }, 180 Attributes: map[string]*configschema.Attribute{ 181 "bloop": { 182 NestedType: &configschema.Object{ 183 Nesting: configschema.NestingSet, 184 Attributes: map[string]*configschema.Attribute{ 185 "blop": { 186 Type: cty.String, 187 Required: true, 188 }, 189 }, 190 }, 191 Computed: true, 192 Optional: true, 193 }, 194 }, 195 }, 196 cty.NullVal(cty.DynamicPseudoType), 197 cty.ObjectVal(map[string]cty.Value{ 198 "baz": cty.SetVal([]cty.Value{ 199 cty.ObjectVal(map[string]cty.Value{ 200 "boz": cty.StringVal("world"), 201 }), 202 }), 203 "bloop": cty.SetVal([]cty.Value{ 204 cty.ObjectVal(map[string]cty.Value{ 205 "blop": cty.StringVal("blub"), 206 }), 207 }), 208 }), 209 cty.ObjectVal(map[string]cty.Value{ 210 "baz": cty.SetVal([]cty.Value{ 211 cty.ObjectVal(map[string]cty.Value{ 212 "boz": cty.StringVal("world"), 213 }), 214 }), 215 "bloop": cty.SetVal([]cty.Value{ 216 cty.ObjectVal(map[string]cty.Value{ 217 "blop": cty.StringVal("blub"), 218 }), 219 }), 220 }), 221 }, 222 "prior attributes": { 223 &configschema.Block{ 224 Attributes: map[string]*configschema.Attribute{ 225 "foo": { 226 Type: cty.String, 227 Optional: true, 228 }, 229 "bar": { 230 Type: cty.String, 231 Computed: true, 232 }, 233 "baz": { 234 Type: cty.String, 235 Optional: true, 236 Computed: true, 237 }, 238 "boz": { 239 Type: cty.String, 240 Optional: true, 241 Computed: true, 242 }, 243 "bloop": { 244 NestedType: &configschema.Object{ 245 Nesting: configschema.NestingSingle, 246 Attributes: map[string]*configschema.Attribute{ 247 "blop": { 248 Type: cty.String, 249 Required: true, 250 }, 251 }, 252 }, 253 Optional: true, 254 }, 255 }, 256 }, 257 cty.ObjectVal(map[string]cty.Value{ 258 "foo": cty.StringVal("bonjour"), 259 "bar": cty.StringVal("petit dejeuner"), 260 "baz": cty.StringVal("grande dejeuner"), 261 "boz": cty.StringVal("a la monde"), 262 "bloop": cty.ObjectVal(map[string]cty.Value{ 263 "blop": cty.StringVal("glub"), 264 }), 265 }), 266 cty.ObjectVal(map[string]cty.Value{ 267 "foo": cty.StringVal("hello"), 268 "bar": cty.NullVal(cty.String), 269 "baz": cty.NullVal(cty.String), 270 "boz": cty.StringVal("world"), 271 "bloop": cty.ObjectVal(map[string]cty.Value{ 272 "blop": cty.StringVal("bleep"), 273 }), 274 }), 275 cty.ObjectVal(map[string]cty.Value{ 276 "foo": cty.StringVal("hello"), 277 "bar": cty.StringVal("petit dejeuner"), 278 "baz": cty.StringVal("grande dejeuner"), 279 "boz": cty.StringVal("world"), 280 "bloop": cty.ObjectVal(map[string]cty.Value{ 281 "blop": cty.StringVal("bleep"), 282 }), 283 }), 284 }, 285 "prior nested single": { 286 &configschema.Block{ 287 BlockTypes: map[string]*configschema.NestedBlock{ 288 "foo": { 289 Nesting: configschema.NestingSingle, 290 Block: configschema.Block{ 291 Attributes: map[string]*configschema.Attribute{ 292 "bar": { 293 Type: cty.String, 294 Optional: true, 295 Computed: true, 296 }, 297 "baz": { 298 Type: cty.String, 299 Optional: true, 300 Computed: true, 301 }, 302 }, 303 }, 304 }, 305 }, 306 Attributes: map[string]*configschema.Attribute{ 307 "bloop": { 308 NestedType: &configschema.Object{ 309 Nesting: configschema.NestingSingle, 310 Attributes: map[string]*configschema.Attribute{ 311 "blop": { 312 Type: cty.String, 313 Required: true, 314 }, 315 "bleep": { 316 Type: cty.String, 317 Optional: true, 318 }, 319 }, 320 }, 321 Optional: true, 322 }, 323 }, 324 }, 325 cty.ObjectVal(map[string]cty.Value{ 326 "foo": cty.ObjectVal(map[string]cty.Value{ 327 "bar": cty.StringVal("beep"), 328 "baz": cty.StringVal("boop"), 329 }), 330 "bloop": cty.ObjectVal(map[string]cty.Value{ 331 "blop": cty.StringVal("glub"), 332 "bleep": cty.NullVal(cty.String), 333 }), 334 }), 335 cty.ObjectVal(map[string]cty.Value{ 336 "foo": cty.ObjectVal(map[string]cty.Value{ 337 "bar": cty.StringVal("bap"), 338 "baz": cty.NullVal(cty.String), 339 }), 340 "bloop": cty.ObjectVal(map[string]cty.Value{ 341 "blop": cty.StringVal("glub"), 342 "bleep": cty.StringVal("beep"), 343 }), 344 }), 345 cty.ObjectVal(map[string]cty.Value{ 346 "foo": cty.ObjectVal(map[string]cty.Value{ 347 "bar": cty.StringVal("bap"), 348 "baz": cty.StringVal("boop"), 349 }), 350 "bloop": cty.ObjectVal(map[string]cty.Value{ 351 "blop": cty.StringVal("glub"), 352 "bleep": cty.StringVal("beep"), 353 }), 354 }), 355 }, 356 "prior nested single to null": { 357 &configschema.Block{ 358 BlockTypes: map[string]*configschema.NestedBlock{ 359 "foo": { 360 Nesting: configschema.NestingSingle, 361 Block: configschema.Block{ 362 Attributes: map[string]*configschema.Attribute{ 363 "bar": { 364 Type: cty.String, 365 Optional: true, 366 Computed: true, 367 }, 368 "baz": { 369 Type: cty.String, 370 Optional: true, 371 Computed: true, 372 }, 373 }, 374 }, 375 }, 376 }, 377 Attributes: map[string]*configschema.Attribute{ 378 "bloop": { 379 NestedType: &configschema.Object{ 380 Nesting: configschema.NestingSingle, 381 Attributes: map[string]*configschema.Attribute{ 382 "blop": { 383 Type: cty.String, 384 Required: true, 385 }, 386 "bleep": { 387 Type: cty.String, 388 Optional: true, 389 }, 390 }, 391 }, 392 Optional: true, 393 }, 394 }, 395 }, 396 cty.ObjectVal(map[string]cty.Value{ 397 "foo": cty.ObjectVal(map[string]cty.Value{ 398 "bar": cty.StringVal("beep"), 399 "baz": cty.StringVal("boop"), 400 }), 401 "bloop": cty.ObjectVal(map[string]cty.Value{ 402 "blop": cty.StringVal("glub"), 403 "bleep": cty.NullVal(cty.String), 404 }), 405 }), 406 cty.ObjectVal(map[string]cty.Value{ 407 "foo": cty.NullVal(cty.Object(map[string]cty.Type{ 408 "bar": cty.String, 409 "baz": cty.String, 410 })), 411 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 412 "blop": cty.String, 413 "bleep": cty.String, 414 })), 415 }), 416 cty.ObjectVal(map[string]cty.Value{ 417 "foo": cty.NullVal(cty.Object(map[string]cty.Type{ 418 "bar": cty.String, 419 "baz": cty.String, 420 })), 421 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 422 "blop": cty.String, 423 "bleep": cty.String, 424 })), 425 }), 426 }, 427 428 "prior optional computed nested single to null": { 429 &configschema.Block{ 430 Attributes: map[string]*configschema.Attribute{ 431 "bloop": { 432 NestedType: &configschema.Object{ 433 Nesting: configschema.NestingSingle, 434 Attributes: map[string]*configschema.Attribute{ 435 "blop": { 436 Type: cty.String, 437 Required: true, 438 }, 439 "bleep": { 440 Type: cty.String, 441 Optional: true, 442 }, 443 }, 444 }, 445 Optional: true, 446 Computed: true, 447 }, 448 }, 449 }, 450 cty.ObjectVal(map[string]cty.Value{ 451 "bloop": cty.ObjectVal(map[string]cty.Value{ 452 "blop": cty.StringVal("glub"), 453 "bleep": cty.NullVal(cty.String), 454 }), 455 }), 456 cty.ObjectVal(map[string]cty.Value{ 457 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 458 "blop": cty.String, 459 "bleep": cty.String, 460 })), 461 }), 462 cty.ObjectVal(map[string]cty.Value{ 463 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 464 "blop": cty.String, 465 "bleep": cty.String, 466 })), 467 }), 468 }, 469 470 "prior nested list": { 471 &configschema.Block{ 472 BlockTypes: map[string]*configschema.NestedBlock{ 473 "foo": { 474 Nesting: configschema.NestingList, 475 Block: configschema.Block{ 476 Attributes: map[string]*configschema.Attribute{ 477 "bar": { 478 Type: cty.String, 479 Optional: true, 480 Computed: true, 481 }, 482 "baz": { 483 Type: cty.String, 484 Optional: true, 485 Computed: true, 486 }, 487 }, 488 }, 489 }, 490 }, 491 Attributes: map[string]*configschema.Attribute{ 492 "bloop": { 493 NestedType: &configschema.Object{ 494 Nesting: configschema.NestingList, 495 Attributes: map[string]*configschema.Attribute{ 496 "blop": { 497 Type: cty.String, 498 Required: true, 499 }, 500 }, 501 }, 502 Optional: true, 503 }, 504 }, 505 }, 506 cty.ObjectVal(map[string]cty.Value{ 507 "foo": cty.ListVal([]cty.Value{ 508 cty.ObjectVal(map[string]cty.Value{ 509 "bar": cty.StringVal("beep"), 510 "baz": cty.StringVal("boop"), 511 }), 512 }), 513 "bloop": cty.ListVal([]cty.Value{ 514 cty.ObjectVal(map[string]cty.Value{ 515 "blop": cty.StringVal("bar"), 516 }), 517 cty.ObjectVal(map[string]cty.Value{ 518 "blop": cty.StringVal("baz"), 519 }), 520 }), 521 }), 522 cty.ObjectVal(map[string]cty.Value{ 523 "foo": cty.ListVal([]cty.Value{ 524 cty.ObjectVal(map[string]cty.Value{ 525 "bar": cty.StringVal("bap"), 526 "baz": cty.NullVal(cty.String), 527 }), 528 cty.ObjectVal(map[string]cty.Value{ 529 "bar": cty.StringVal("blep"), 530 "baz": cty.NullVal(cty.String), 531 }), 532 }), 533 "bloop": cty.ListVal([]cty.Value{ 534 cty.ObjectVal(map[string]cty.Value{ 535 "blop": cty.StringVal("bar"), 536 }), 537 cty.ObjectVal(map[string]cty.Value{ 538 "blop": cty.StringVal("baz"), 539 }), 540 }), 541 }), 542 cty.ObjectVal(map[string]cty.Value{ 543 "foo": cty.ListVal([]cty.Value{ 544 cty.ObjectVal(map[string]cty.Value{ 545 "bar": cty.StringVal("bap"), 546 "baz": cty.StringVal("boop"), 547 }), 548 cty.ObjectVal(map[string]cty.Value{ 549 "bar": cty.StringVal("blep"), 550 "baz": cty.NullVal(cty.String), 551 }), 552 }), 553 "bloop": cty.ListVal([]cty.Value{ 554 cty.ObjectVal(map[string]cty.Value{ 555 "blop": cty.StringVal("bar"), 556 }), 557 cty.ObjectVal(map[string]cty.Value{ 558 "blop": cty.StringVal("baz"), 559 }), 560 }), 561 }), 562 }, 563 "prior nested list with dynamic": { 564 &configschema.Block{ 565 BlockTypes: map[string]*configschema.NestedBlock{ 566 "foo": { 567 Nesting: configschema.NestingList, 568 Block: configschema.Block{ 569 Attributes: map[string]*configschema.Attribute{ 570 "bar": { 571 Type: cty.String, 572 Optional: true, 573 Computed: true, 574 }, 575 "baz": { 576 Type: cty.DynamicPseudoType, 577 Optional: true, 578 Computed: true, 579 }, 580 }, 581 }, 582 }, 583 }, 584 Attributes: map[string]*configschema.Attribute{ 585 "bloop": { 586 NestedType: &configschema.Object{ 587 Nesting: configschema.NestingList, 588 Attributes: map[string]*configschema.Attribute{ 589 "blop": { 590 Type: cty.DynamicPseudoType, 591 Required: true, 592 }, 593 "blub": { 594 Type: cty.DynamicPseudoType, 595 Optional: true, 596 }, 597 }, 598 }, 599 Optional: true, 600 }, 601 }, 602 }, 603 cty.ObjectVal(map[string]cty.Value{ 604 "foo": cty.TupleVal([]cty.Value{ 605 cty.ObjectVal(map[string]cty.Value{ 606 "bar": cty.StringVal("beep"), 607 "baz": cty.StringVal("boop"), 608 }), 609 }), 610 "bloop": cty.ListVal([]cty.Value{ 611 cty.ObjectVal(map[string]cty.Value{ 612 "blop": cty.StringVal("bar"), 613 "blub": cty.StringVal("glub"), 614 }), 615 cty.ObjectVal(map[string]cty.Value{ 616 "blop": cty.StringVal("baz"), 617 "blub": cty.NullVal(cty.String), 618 }), 619 }), 620 }), 621 cty.ObjectVal(map[string]cty.Value{ 622 "foo": cty.TupleVal([]cty.Value{ 623 cty.ObjectVal(map[string]cty.Value{ 624 "bar": cty.StringVal("bap"), 625 "baz": cty.NullVal(cty.String), 626 }), 627 cty.ObjectVal(map[string]cty.Value{ 628 "bar": cty.StringVal("blep"), 629 "baz": cty.NullVal(cty.String), 630 }), 631 }), 632 "bloop": cty.ListVal([]cty.Value{ 633 cty.ObjectVal(map[string]cty.Value{ 634 "blop": cty.StringVal("bar"), 635 "blub": cty.NullVal(cty.String), 636 }), 637 }), 638 }), 639 cty.ObjectVal(map[string]cty.Value{ 640 "foo": cty.TupleVal([]cty.Value{ 641 cty.ObjectVal(map[string]cty.Value{ 642 "bar": cty.StringVal("bap"), 643 "baz": cty.StringVal("boop"), 644 }), 645 cty.ObjectVal(map[string]cty.Value{ 646 "bar": cty.StringVal("blep"), 647 "baz": cty.NullVal(cty.String), 648 }), 649 }), 650 "bloop": cty.ListVal([]cty.Value{ 651 cty.ObjectVal(map[string]cty.Value{ 652 "blop": cty.StringVal("bar"), 653 "blub": cty.NullVal(cty.String), 654 }), 655 }), 656 }), 657 }, 658 "prior nested map": { 659 &configschema.Block{ 660 BlockTypes: map[string]*configschema.NestedBlock{ 661 "foo": { 662 Nesting: configschema.NestingMap, 663 Block: configschema.Block{ 664 Attributes: map[string]*configschema.Attribute{ 665 "bar": { 666 Type: cty.String, 667 Optional: true, 668 Computed: true, 669 }, 670 "baz": { 671 Type: cty.String, 672 Optional: true, 673 Computed: true, 674 }, 675 }, 676 }, 677 }, 678 }, 679 Attributes: map[string]*configschema.Attribute{ 680 "bloop": { 681 NestedType: &configschema.Object{ 682 Nesting: configschema.NestingMap, 683 Attributes: map[string]*configschema.Attribute{ 684 "blop": { 685 Type: cty.String, 686 Required: true, 687 }, 688 }, 689 }, 690 Optional: true, 691 }, 692 }, 693 }, 694 cty.ObjectVal(map[string]cty.Value{ 695 "foo": cty.MapVal(map[string]cty.Value{ 696 "a": cty.ObjectVal(map[string]cty.Value{ 697 "bar": cty.StringVal("beep"), 698 "baz": cty.StringVal("boop"), 699 }), 700 "b": cty.ObjectVal(map[string]cty.Value{ 701 "bar": cty.StringVal("blep"), 702 "baz": cty.StringVal("boot"), 703 }), 704 }), 705 "bloop": cty.MapVal(map[string]cty.Value{ 706 "a": cty.ObjectVal(map[string]cty.Value{ 707 "blop": cty.StringVal("glub"), 708 }), 709 "b": cty.ObjectVal(map[string]cty.Value{ 710 "blop": cty.StringVal("blub"), 711 }), 712 }), 713 }), 714 cty.ObjectVal(map[string]cty.Value{ 715 "foo": cty.MapVal(map[string]cty.Value{ 716 "a": cty.ObjectVal(map[string]cty.Value{ 717 "bar": cty.StringVal("bap"), 718 "baz": cty.NullVal(cty.String), 719 }), 720 "c": cty.ObjectVal(map[string]cty.Value{ 721 "bar": cty.StringVal("bosh"), 722 "baz": cty.NullVal(cty.String), 723 }), 724 }), 725 "bloop": cty.MapVal(map[string]cty.Value{ 726 "a": cty.ObjectVal(map[string]cty.Value{ 727 "blop": cty.StringVal("glub"), 728 }), 729 "c": cty.ObjectVal(map[string]cty.Value{ 730 "blop": cty.StringVal("blub"), 731 }), 732 }), 733 }), 734 cty.ObjectVal(map[string]cty.Value{ 735 "foo": cty.MapVal(map[string]cty.Value{ 736 "a": cty.ObjectVal(map[string]cty.Value{ 737 "bar": cty.StringVal("bap"), 738 "baz": cty.StringVal("boop"), 739 }), 740 "c": cty.ObjectVal(map[string]cty.Value{ 741 "bar": cty.StringVal("bosh"), 742 "baz": cty.NullVal(cty.String), 743 }), 744 }), 745 "bloop": cty.MapVal(map[string]cty.Value{ 746 "a": cty.ObjectVal(map[string]cty.Value{ 747 "blop": cty.StringVal("glub"), 748 }), 749 "c": cty.ObjectVal(map[string]cty.Value{ 750 "blop": cty.StringVal("blub"), 751 }), 752 }), 753 }), 754 }, 755 756 "prior optional computed nested map elem to null": { 757 &configschema.Block{ 758 Attributes: map[string]*configschema.Attribute{ 759 "bloop": { 760 NestedType: &configschema.Object{ 761 Nesting: configschema.NestingMap, 762 Attributes: map[string]*configschema.Attribute{ 763 "blop": { 764 Type: cty.String, 765 Optional: true, 766 }, 767 "bleep": { 768 Type: cty.String, 769 Optional: true, 770 Computed: true, 771 }, 772 }, 773 }, 774 Optional: true, 775 }, 776 }, 777 }, 778 cty.ObjectVal(map[string]cty.Value{ 779 "bloop": cty.MapVal(map[string]cty.Value{ 780 "a": cty.ObjectVal(map[string]cty.Value{ 781 "blop": cty.StringVal("glub"), 782 "bleep": cty.StringVal("computed"), 783 }), 784 "b": cty.ObjectVal(map[string]cty.Value{ 785 "blop": cty.StringVal("blub"), 786 "bleep": cty.StringVal("computed"), 787 }), 788 }), 789 }), 790 cty.ObjectVal(map[string]cty.Value{ 791 "bloop": cty.MapVal(map[string]cty.Value{ 792 "a": cty.NullVal(cty.Object(map[string]cty.Type{ 793 "blop": cty.String, 794 "bleep": cty.String, 795 })), 796 "c": cty.ObjectVal(map[string]cty.Value{ 797 "blop": cty.StringVal("blub"), 798 "bleep": cty.NullVal(cty.String), 799 }), 800 }), 801 }), 802 cty.ObjectVal(map[string]cty.Value{ 803 "bloop": cty.MapVal(map[string]cty.Value{ 804 "a": cty.NullVal(cty.Object(map[string]cty.Type{ 805 "blop": cty.String, 806 "bleep": cty.String, 807 })), 808 "c": cty.ObjectVal(map[string]cty.Value{ 809 "blop": cty.StringVal("blub"), 810 "bleep": cty.NullVal(cty.String), 811 }), 812 }), 813 }), 814 }, 815 816 "prior optional computed nested map to null": { 817 &configschema.Block{ 818 Attributes: map[string]*configschema.Attribute{ 819 "bloop": { 820 NestedType: &configschema.Object{ 821 Nesting: configschema.NestingMap, 822 Attributes: map[string]*configschema.Attribute{ 823 "blop": { 824 Type: cty.String, 825 Optional: true, 826 }, 827 "bleep": { 828 Type: cty.String, 829 Optional: true, 830 Computed: true, 831 }, 832 }, 833 }, 834 Optional: true, 835 Computed: true, 836 }, 837 }, 838 }, 839 cty.ObjectVal(map[string]cty.Value{ 840 "bloop": cty.MapVal(map[string]cty.Value{ 841 "a": cty.ObjectVal(map[string]cty.Value{ 842 "blop": cty.StringVal("glub"), 843 "bleep": cty.StringVal("computed"), 844 }), 845 "b": cty.ObjectVal(map[string]cty.Value{ 846 "blop": cty.StringVal("blub"), 847 "bleep": cty.StringVal("computed"), 848 }), 849 }), 850 }), 851 cty.ObjectVal(map[string]cty.Value{ 852 "bloop": cty.NullVal(cty.Map( 853 cty.Object(map[string]cty.Type{ 854 "blop": cty.String, 855 "bleep": cty.String, 856 }), 857 )), 858 }), 859 cty.ObjectVal(map[string]cty.Value{ 860 "bloop": cty.NullVal(cty.Map( 861 cty.Object(map[string]cty.Type{ 862 "blop": cty.String, 863 "bleep": cty.String, 864 }), 865 )), 866 }), 867 }, 868 869 "prior nested map with dynamic": { 870 &configschema.Block{ 871 BlockTypes: map[string]*configschema.NestedBlock{ 872 "foo": { 873 Nesting: configschema.NestingMap, 874 Block: configschema.Block{ 875 Attributes: map[string]*configschema.Attribute{ 876 "bar": { 877 Type: cty.String, 878 Optional: true, 879 Computed: true, 880 }, 881 "baz": { 882 Type: cty.DynamicPseudoType, 883 Optional: true, 884 Computed: true, 885 }, 886 }, 887 }, 888 }, 889 }, 890 Attributes: map[string]*configschema.Attribute{ 891 "bloop": { 892 NestedType: &configschema.Object{ 893 Nesting: configschema.NestingMap, 894 Attributes: map[string]*configschema.Attribute{ 895 "blop": { 896 Type: cty.DynamicPseudoType, 897 Required: true, 898 }, 899 }, 900 }, 901 Optional: true, 902 }, 903 }, 904 }, 905 cty.ObjectVal(map[string]cty.Value{ 906 "foo": cty.ObjectVal(map[string]cty.Value{ 907 "a": cty.ObjectVal(map[string]cty.Value{ 908 "bar": cty.StringVal("beep"), 909 "baz": cty.StringVal("boop"), 910 }), 911 "b": cty.ObjectVal(map[string]cty.Value{ 912 "bar": cty.StringVal("blep"), 913 "baz": cty.ListVal([]cty.Value{cty.StringVal("boot")}), 914 }), 915 }), 916 "bloop": cty.ObjectVal(map[string]cty.Value{ 917 "a": cty.ObjectVal(map[string]cty.Value{ 918 "blop": cty.StringVal("glub"), 919 }), 920 "b": cty.ObjectVal(map[string]cty.Value{ 921 "blop": cty.NumberIntVal(13), 922 }), 923 }), 924 }), 925 cty.ObjectVal(map[string]cty.Value{ 926 "foo": cty.ObjectVal(map[string]cty.Value{ 927 "a": cty.ObjectVal(map[string]cty.Value{ 928 "bar": cty.StringVal("bap"), 929 "baz": cty.NullVal(cty.String), 930 }), 931 "c": cty.ObjectVal(map[string]cty.Value{ 932 "bar": cty.StringVal("bosh"), 933 "baz": cty.NullVal(cty.List(cty.String)), 934 }), 935 }), 936 "bloop": cty.ObjectVal(map[string]cty.Value{ 937 "a": cty.ObjectVal(map[string]cty.Value{ 938 "blop": cty.StringVal("blep"), 939 }), 940 "c": cty.ObjectVal(map[string]cty.Value{ 941 "blop": cty.NumberIntVal(13), 942 }), 943 }), 944 }), 945 cty.ObjectVal(map[string]cty.Value{ 946 "foo": cty.ObjectVal(map[string]cty.Value{ 947 "a": cty.ObjectVal(map[string]cty.Value{ 948 "bar": cty.StringVal("bap"), 949 "baz": cty.StringVal("boop"), 950 }), 951 "c": cty.ObjectVal(map[string]cty.Value{ 952 "bar": cty.StringVal("bosh"), 953 "baz": cty.NullVal(cty.List(cty.String)), 954 }), 955 }), 956 "bloop": cty.ObjectVal(map[string]cty.Value{ 957 "a": cty.ObjectVal(map[string]cty.Value{ 958 "blop": cty.StringVal("blep"), 959 }), 960 "c": cty.ObjectVal(map[string]cty.Value{ 961 "blop": cty.NumberIntVal(13), 962 }), 963 }), 964 }), 965 }, 966 "prior nested set": { 967 &configschema.Block{ 968 BlockTypes: map[string]*configschema.NestedBlock{ 969 "foo": { 970 Nesting: configschema.NestingSet, 971 Block: configschema.Block{ 972 Attributes: map[string]*configschema.Attribute{ 973 "bar": { 974 // This non-computed attribute will serve 975 // as our matching key for propagating 976 // "baz" from elements in the prior value. 977 Type: cty.String, 978 Optional: true, 979 }, 980 "baz": { 981 Type: cty.String, 982 Optional: true, 983 Computed: true, 984 }, 985 }, 986 }, 987 }, 988 }, 989 Attributes: map[string]*configschema.Attribute{ 990 "bloop": { 991 NestedType: &configschema.Object{ 992 Nesting: configschema.NestingSet, 993 Attributes: map[string]*configschema.Attribute{ 994 "blop": { 995 Type: cty.String, 996 Required: true, 997 }, 998 "bleep": { 999 Type: cty.String, 1000 Optional: true, 1001 }, 1002 }, 1003 }, 1004 Optional: true, 1005 }, 1006 }, 1007 }, 1008 cty.ObjectVal(map[string]cty.Value{ 1009 "foo": cty.SetVal([]cty.Value{ 1010 cty.ObjectVal(map[string]cty.Value{ 1011 "bar": cty.StringVal("beep"), 1012 "baz": cty.StringVal("boop"), 1013 }), 1014 cty.ObjectVal(map[string]cty.Value{ 1015 "bar": cty.StringVal("blep"), 1016 "baz": cty.StringVal("boot"), 1017 }), 1018 }), 1019 "bloop": cty.SetVal([]cty.Value{ 1020 cty.ObjectVal(map[string]cty.Value{ 1021 "blop": cty.StringVal("glubglub"), 1022 "bleep": cty.NullVal(cty.String), 1023 }), 1024 cty.ObjectVal(map[string]cty.Value{ 1025 "blop": cty.StringVal("glubglub"), 1026 "bleep": cty.StringVal("beep"), 1027 }), 1028 }), 1029 }), 1030 cty.ObjectVal(map[string]cty.Value{ 1031 "foo": cty.SetVal([]cty.Value{ 1032 cty.ObjectVal(map[string]cty.Value{ 1033 "bar": cty.StringVal("beep"), 1034 "baz": cty.NullVal(cty.String), 1035 }), 1036 cty.ObjectVal(map[string]cty.Value{ 1037 "bar": cty.StringVal("bosh"), 1038 "baz": cty.NullVal(cty.String), 1039 }), 1040 }), 1041 "bloop": cty.SetVal([]cty.Value{ 1042 cty.ObjectVal(map[string]cty.Value{ 1043 "blop": cty.StringVal("glubglub"), 1044 "bleep": cty.NullVal(cty.String), 1045 }), 1046 cty.ObjectVal(map[string]cty.Value{ 1047 "blop": cty.StringVal("glub"), 1048 "bleep": cty.NullVal(cty.String), 1049 }), 1050 }), 1051 }), 1052 cty.ObjectVal(map[string]cty.Value{ 1053 "foo": cty.SetVal([]cty.Value{ 1054 cty.ObjectVal(map[string]cty.Value{ 1055 "bar": cty.StringVal("beep"), 1056 "baz": cty.StringVal("boop"), 1057 }), 1058 cty.ObjectVal(map[string]cty.Value{ 1059 "bar": cty.StringVal("bosh"), 1060 "baz": cty.NullVal(cty.String), 1061 }), 1062 }), 1063 "bloop": cty.SetVal([]cty.Value{ 1064 cty.ObjectVal(map[string]cty.Value{ 1065 "blop": cty.StringVal("glubglub"), 1066 "bleep": cty.NullVal(cty.String), 1067 }), 1068 cty.ObjectVal(map[string]cty.Value{ 1069 "blop": cty.StringVal("glub"), 1070 "bleep": cty.NullVal(cty.String), 1071 }), 1072 }), 1073 }), 1074 }, 1075 1076 "set with partial optional computed change": { 1077 &configschema.Block{ 1078 BlockTypes: map[string]*configschema.NestedBlock{ 1079 "multi": { 1080 Nesting: configschema.NestingSet, 1081 Block: configschema.Block{ 1082 Attributes: map[string]*configschema.Attribute{ 1083 "opt": { 1084 Type: cty.String, 1085 Optional: true, 1086 }, 1087 "cmp": { 1088 Type: cty.String, 1089 Optional: true, 1090 Computed: true, 1091 }, 1092 }, 1093 }, 1094 }, 1095 }, 1096 }, 1097 cty.ObjectVal(map[string]cty.Value{ 1098 "multi": cty.SetVal([]cty.Value{ 1099 cty.ObjectVal(map[string]cty.Value{ 1100 "opt": cty.StringVal("one"), 1101 "cmp": cty.StringVal("OK"), 1102 }), 1103 cty.ObjectVal(map[string]cty.Value{ 1104 "opt": cty.StringVal("two"), 1105 "cmp": cty.StringVal("OK"), 1106 }), 1107 }), 1108 }), 1109 1110 cty.ObjectVal(map[string]cty.Value{ 1111 "multi": cty.SetVal([]cty.Value{ 1112 cty.ObjectVal(map[string]cty.Value{ 1113 "opt": cty.StringVal("one"), 1114 "cmp": cty.NullVal(cty.String), 1115 }), 1116 cty.ObjectVal(map[string]cty.Value{ 1117 "opt": cty.StringVal("replaced"), 1118 "cmp": cty.NullVal(cty.String), 1119 }), 1120 }), 1121 }), 1122 // "one" can be correlated because it is a non-computed value in 1123 // the configuration. 1124 cty.ObjectVal(map[string]cty.Value{ 1125 "multi": cty.SetVal([]cty.Value{ 1126 cty.ObjectVal(map[string]cty.Value{ 1127 "opt": cty.StringVal("one"), 1128 "cmp": cty.StringVal("OK"), 1129 }), 1130 cty.ObjectVal(map[string]cty.Value{ 1131 "opt": cty.StringVal("replaced"), 1132 "cmp": cty.NullVal(cty.String), 1133 }), 1134 }), 1135 }), 1136 }, 1137 1138 "set without partial optional computed change": { 1139 &configschema.Block{ 1140 BlockTypes: map[string]*configschema.NestedBlock{ 1141 "multi": { 1142 Nesting: configschema.NestingSet, 1143 Block: configschema.Block{ 1144 Attributes: map[string]*configschema.Attribute{ 1145 "opt": { 1146 Type: cty.String, 1147 Optional: true, 1148 Computed: true, 1149 }, 1150 "req": { 1151 Type: cty.String, 1152 Required: true, 1153 }, 1154 }, 1155 }, 1156 }, 1157 }, 1158 }, 1159 cty.ObjectVal(map[string]cty.Value{ 1160 "multi": cty.SetVal([]cty.Value{ 1161 cty.ObjectVal(map[string]cty.Value{ 1162 "opt": cty.StringVal("one"), 1163 "req": cty.StringVal("one"), 1164 }), 1165 cty.ObjectVal(map[string]cty.Value{ 1166 "opt": cty.StringVal("two"), 1167 "req": cty.StringVal("two"), 1168 }), 1169 }), 1170 }), 1171 cty.ObjectVal(map[string]cty.Value{ 1172 "multi": cty.SetVal([]cty.Value{ 1173 cty.ObjectVal(map[string]cty.Value{ 1174 "opt": cty.NullVal(cty.String), 1175 "req": cty.StringVal("one"), 1176 }), 1177 cty.ObjectVal(map[string]cty.Value{ 1178 "opt": cty.NullVal(cty.String), 1179 "req": cty.StringVal("two"), 1180 }), 1181 }), 1182 }), 1183 cty.ObjectVal(map[string]cty.Value{ 1184 "multi": cty.SetVal([]cty.Value{ 1185 cty.ObjectVal(map[string]cty.Value{ 1186 "opt": cty.StringVal("one"), 1187 "req": cty.StringVal("one"), 1188 }), 1189 cty.ObjectVal(map[string]cty.Value{ 1190 "opt": cty.StringVal("two"), 1191 "req": cty.StringVal("two"), 1192 }), 1193 }), 1194 }), 1195 }, 1196 1197 "sets differing only by unknown": { 1198 &configschema.Block{ 1199 BlockTypes: map[string]*configschema.NestedBlock{ 1200 "multi": { 1201 Nesting: configschema.NestingSet, 1202 Block: configschema.Block{ 1203 Attributes: map[string]*configschema.Attribute{ 1204 "optional": { 1205 Type: cty.String, 1206 Optional: true, 1207 Computed: true, 1208 }, 1209 }, 1210 }, 1211 }, 1212 }, 1213 Attributes: map[string]*configschema.Attribute{ 1214 "bloop": { 1215 NestedType: &configschema.Object{ 1216 Nesting: configschema.NestingSet, 1217 Attributes: map[string]*configschema.Attribute{ 1218 "blop": { 1219 Type: cty.String, 1220 Required: true, 1221 }, 1222 }, 1223 }, 1224 Optional: true, 1225 }, 1226 }, 1227 }, 1228 cty.NullVal(cty.DynamicPseudoType), 1229 cty.ObjectVal(map[string]cty.Value{ 1230 "multi": cty.SetVal([]cty.Value{ 1231 cty.ObjectVal(map[string]cty.Value{ 1232 "optional": cty.UnknownVal(cty.String), 1233 }), 1234 cty.ObjectVal(map[string]cty.Value{ 1235 "optional": cty.UnknownVal(cty.String), 1236 }), 1237 }), 1238 "bloop": cty.SetVal([]cty.Value{ 1239 cty.ObjectVal(map[string]cty.Value{ 1240 "blop": cty.UnknownVal(cty.String), 1241 }), 1242 cty.ObjectVal(map[string]cty.Value{ 1243 "blop": cty.UnknownVal(cty.String), 1244 }), 1245 }), 1246 }), 1247 cty.ObjectVal(map[string]cty.Value{ 1248 "multi": cty.SetVal([]cty.Value{ 1249 // These remain distinct because unknown values never 1250 // compare equal. They may be consolidated together once 1251 // the values become known, though. 1252 cty.ObjectVal(map[string]cty.Value{ 1253 "optional": cty.UnknownVal(cty.String), 1254 }), 1255 cty.ObjectVal(map[string]cty.Value{ 1256 "optional": cty.UnknownVal(cty.String), 1257 }), 1258 }), 1259 "bloop": cty.SetVal([]cty.Value{ 1260 cty.ObjectVal(map[string]cty.Value{ 1261 "blop": cty.UnknownVal(cty.String), 1262 }), 1263 cty.ObjectVal(map[string]cty.Value{ 1264 "blop": cty.UnknownVal(cty.String), 1265 }), 1266 }), 1267 }), 1268 }, 1269 "nested list in set": { 1270 &configschema.Block{ 1271 BlockTypes: map[string]*configschema.NestedBlock{ 1272 "foo": { 1273 Nesting: configschema.NestingSet, 1274 Block: configschema.Block{ 1275 BlockTypes: map[string]*configschema.NestedBlock{ 1276 "bar": { 1277 Nesting: configschema.NestingList, 1278 Block: configschema.Block{ 1279 Attributes: map[string]*configschema.Attribute{ 1280 "baz": { 1281 Type: cty.String, 1282 }, 1283 "qux": { 1284 Type: cty.String, 1285 Computed: true, 1286 Optional: true, 1287 }, 1288 }, 1289 }, 1290 }, 1291 }, 1292 }, 1293 }, 1294 }, 1295 }, 1296 cty.ObjectVal(map[string]cty.Value{ 1297 "foo": cty.SetVal([]cty.Value{ 1298 cty.ObjectVal(map[string]cty.Value{ 1299 "bar": cty.ListVal([]cty.Value{ 1300 cty.ObjectVal(map[string]cty.Value{ 1301 "baz": cty.StringVal("beep"), 1302 "qux": cty.StringVal("boop"), 1303 }), 1304 }), 1305 }), 1306 }), 1307 }), 1308 cty.ObjectVal(map[string]cty.Value{ 1309 "foo": cty.SetVal([]cty.Value{ 1310 cty.ObjectVal(map[string]cty.Value{ 1311 "bar": cty.ListVal([]cty.Value{ 1312 cty.ObjectVal(map[string]cty.Value{ 1313 "baz": cty.StringVal("beep"), 1314 "qux": cty.NullVal(cty.String), 1315 }), 1316 }), 1317 }), 1318 }), 1319 }), 1320 cty.ObjectVal(map[string]cty.Value{ 1321 "foo": cty.SetVal([]cty.Value{ 1322 cty.ObjectVal(map[string]cty.Value{ 1323 "bar": cty.ListVal([]cty.Value{ 1324 cty.ObjectVal(map[string]cty.Value{ 1325 "baz": cty.StringVal("beep"), 1326 "qux": cty.StringVal("boop"), 1327 }), 1328 }), 1329 }), 1330 }), 1331 }), 1332 }, 1333 "empty nested list in set": { 1334 &configschema.Block{ 1335 BlockTypes: map[string]*configschema.NestedBlock{ 1336 "foo": { 1337 Nesting: configschema.NestingSet, 1338 Block: configschema.Block{ 1339 BlockTypes: map[string]*configschema.NestedBlock{ 1340 "bar": { 1341 Nesting: configschema.NestingList, 1342 Block: configschema.Block{}, 1343 }, 1344 }, 1345 }, 1346 }, 1347 }, 1348 }, 1349 cty.ObjectVal(map[string]cty.Value{ 1350 "foo": cty.SetVal([]cty.Value{ 1351 cty.ObjectVal(map[string]cty.Value{ 1352 "bar": cty.ListValEmpty((&configschema.Block{}).ImpliedType()), 1353 }), 1354 }), 1355 }), 1356 cty.ObjectVal(map[string]cty.Value{ 1357 "foo": cty.SetVal([]cty.Value{ 1358 cty.ObjectVal(map[string]cty.Value{ 1359 "bar": cty.ListValEmpty((&configschema.Block{}).ImpliedType()), 1360 }), 1361 }), 1362 }), 1363 cty.ObjectVal(map[string]cty.Value{ 1364 "foo": cty.SetVal([]cty.Value{ 1365 cty.ObjectVal(map[string]cty.Value{ 1366 "bar": cty.ListValEmpty((&configschema.Block{}).ImpliedType()), 1367 }), 1368 }), 1369 }), 1370 }, 1371 "nested list with dynamic in set": { 1372 &configschema.Block{ 1373 BlockTypes: map[string]*configschema.NestedBlock{ 1374 "foo": { 1375 Nesting: configschema.NestingSet, 1376 Block: configschema.Block{ 1377 BlockTypes: map[string]*configschema.NestedBlock{ 1378 "bar": { 1379 Nesting: configschema.NestingList, 1380 Block: configschema.Block{ 1381 Attributes: map[string]*configschema.Attribute{ 1382 "baz": { 1383 Type: cty.DynamicPseudoType, 1384 }, 1385 }, 1386 }, 1387 }, 1388 }, 1389 }, 1390 }, 1391 }, 1392 }, 1393 cty.ObjectVal(map[string]cty.Value{ 1394 "foo": cty.SetVal([]cty.Value{ 1395 cty.ObjectVal(map[string]cty.Value{ 1396 "bar": cty.TupleVal([]cty.Value{ 1397 cty.ObjectVal(map[string]cty.Value{ 1398 "baz": cty.StringVal("true"), 1399 }), 1400 cty.ObjectVal(map[string]cty.Value{ 1401 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1402 }), 1403 }), 1404 }), 1405 }), 1406 }), 1407 cty.ObjectVal(map[string]cty.Value{ 1408 "foo": cty.SetVal([]cty.Value{ 1409 cty.ObjectVal(map[string]cty.Value{ 1410 "bar": cty.TupleVal([]cty.Value{ 1411 cty.ObjectVal(map[string]cty.Value{ 1412 "baz": cty.StringVal("true"), 1413 }), 1414 cty.ObjectVal(map[string]cty.Value{ 1415 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1416 }), 1417 }), 1418 }), 1419 }), 1420 }), 1421 cty.ObjectVal(map[string]cty.Value{ 1422 "foo": cty.SetVal([]cty.Value{ 1423 cty.ObjectVal(map[string]cty.Value{ 1424 "bar": cty.TupleVal([]cty.Value{ 1425 cty.ObjectVal(map[string]cty.Value{ 1426 "baz": cty.StringVal("true"), 1427 }), 1428 cty.ObjectVal(map[string]cty.Value{ 1429 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1430 }), 1431 }), 1432 }), 1433 }), 1434 }), 1435 }, 1436 "nested map with dynamic in set": { 1437 &configschema.Block{ 1438 BlockTypes: map[string]*configschema.NestedBlock{ 1439 "foo": { 1440 Nesting: configschema.NestingSet, 1441 Block: configschema.Block{ 1442 BlockTypes: map[string]*configschema.NestedBlock{ 1443 "bar": { 1444 Nesting: configschema.NestingMap, 1445 Block: configschema.Block{ 1446 Attributes: map[string]*configschema.Attribute{ 1447 "baz": { 1448 Type: cty.DynamicPseudoType, 1449 Optional: true, 1450 }, 1451 }, 1452 }, 1453 }, 1454 }, 1455 }, 1456 }, 1457 }, 1458 }, 1459 cty.ObjectVal(map[string]cty.Value{ 1460 "foo": cty.SetVal([]cty.Value{ 1461 cty.ObjectVal(map[string]cty.Value{ 1462 "bar": cty.ObjectVal(map[string]cty.Value{ 1463 "bing": cty.ObjectVal(map[string]cty.Value{ 1464 "baz": cty.StringVal("true"), 1465 }), 1466 "bang": cty.ObjectVal(map[string]cty.Value{ 1467 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1468 }), 1469 }), 1470 }), 1471 }), 1472 }), 1473 cty.ObjectVal(map[string]cty.Value{ 1474 "foo": cty.SetVal([]cty.Value{ 1475 cty.ObjectVal(map[string]cty.Value{ 1476 "bar": cty.ObjectVal(map[string]cty.Value{ 1477 "bing": cty.ObjectVal(map[string]cty.Value{ 1478 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1479 }), 1480 }), 1481 }), 1482 }), 1483 }), 1484 cty.ObjectVal(map[string]cty.Value{ 1485 "foo": cty.SetVal([]cty.Value{ 1486 cty.ObjectVal(map[string]cty.Value{ 1487 "bar": cty.ObjectVal(map[string]cty.Value{ 1488 "bing": cty.ObjectVal(map[string]cty.Value{ 1489 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1490 }), 1491 }), 1492 }), 1493 }), 1494 }), 1495 }, 1496 "empty nested map in set": { 1497 &configschema.Block{ 1498 BlockTypes: map[string]*configschema.NestedBlock{ 1499 "foo": { 1500 Nesting: configschema.NestingSet, 1501 Block: configschema.Block{ 1502 BlockTypes: map[string]*configschema.NestedBlock{ 1503 "bar": { 1504 Nesting: configschema.NestingMap, 1505 Block: configschema.Block{ 1506 Attributes: map[string]*configschema.Attribute{ 1507 "baz": { 1508 Type: cty.String, 1509 Optional: true, 1510 }, 1511 }, 1512 }, 1513 }, 1514 }, 1515 }, 1516 }, 1517 }, 1518 }, 1519 cty.ObjectVal(map[string]cty.Value{ 1520 "foo": cty.SetVal([]cty.Value{ 1521 cty.ObjectVal(map[string]cty.Value{ 1522 "bar": cty.MapValEmpty(cty.Object(map[string]cty.Type{ 1523 "baz": cty.String, 1524 })), 1525 }), 1526 }), 1527 }), 1528 cty.ObjectVal(map[string]cty.Value{ 1529 "foo": cty.SetVal([]cty.Value{ 1530 cty.ObjectVal(map[string]cty.Value{ 1531 "bar": cty.MapVal(map[string]cty.Value{ 1532 "bing": cty.ObjectVal(map[string]cty.Value{ 1533 "baz": cty.StringVal("true"), 1534 }), 1535 }), 1536 }), 1537 }), 1538 }), 1539 cty.ObjectVal(map[string]cty.Value{ 1540 "foo": cty.SetVal([]cty.Value{ 1541 cty.ObjectVal(map[string]cty.Value{ 1542 "bar": cty.MapVal(map[string]cty.Value{ 1543 "bing": cty.ObjectVal(map[string]cty.Value{ 1544 "baz": cty.StringVal("true"), 1545 }), 1546 }), 1547 }), 1548 }), 1549 }), 1550 }, 1551 // This example has a mixture of optional, computed and required in a deeply-nested NestedType attribute 1552 "deeply NestedType": { 1553 &configschema.Block{ 1554 Attributes: map[string]*configschema.Attribute{ 1555 "foo": { 1556 NestedType: &configschema.Object{ 1557 Nesting: configschema.NestingSingle, 1558 Attributes: map[string]*configschema.Attribute{ 1559 "bar": { 1560 NestedType: &configschema.Object{ 1561 Nesting: configschema.NestingSingle, 1562 Attributes: testAttributes, 1563 }, 1564 Required: true, 1565 }, 1566 "baz": { 1567 NestedType: &configschema.Object{ 1568 Nesting: configschema.NestingSingle, 1569 Attributes: testAttributes, 1570 }, 1571 Optional: true, 1572 }, 1573 }, 1574 }, 1575 Optional: true, 1576 }, 1577 }, 1578 }, 1579 // prior 1580 cty.ObjectVal(map[string]cty.Value{ 1581 "foo": cty.ObjectVal(map[string]cty.Value{ 1582 "bar": cty.NullVal(cty.DynamicPseudoType), 1583 "baz": cty.ObjectVal(map[string]cty.Value{ 1584 "optional": cty.NullVal(cty.String), 1585 "computed": cty.StringVal("hello"), 1586 "optional_computed": cty.StringVal("prior"), 1587 "required": cty.StringVal("present"), 1588 }), 1589 }), 1590 }), 1591 // config 1592 cty.ObjectVal(map[string]cty.Value{ 1593 "foo": cty.ObjectVal(map[string]cty.Value{ 1594 "bar": cty.UnknownVal(cty.Object(map[string]cty.Type{ // explicit unknown from the config 1595 "optional": cty.String, 1596 "computed": cty.String, 1597 "optional_computed": cty.String, 1598 "required": cty.String, 1599 })), 1600 "baz": cty.ObjectVal(map[string]cty.Value{ 1601 "optional": cty.NullVal(cty.String), 1602 "computed": cty.NullVal(cty.String), 1603 "optional_computed": cty.StringVal("hello"), 1604 "required": cty.StringVal("present"), 1605 }), 1606 }), 1607 }), 1608 // want 1609 cty.ObjectVal(map[string]cty.Value{ 1610 "foo": cty.ObjectVal(map[string]cty.Value{ 1611 "bar": cty.UnknownVal(cty.Object(map[string]cty.Type{ // explicit unknown preserved from the config 1612 "optional": cty.String, 1613 "computed": cty.String, 1614 "optional_computed": cty.String, 1615 "required": cty.String, 1616 })), 1617 "baz": cty.ObjectVal(map[string]cty.Value{ 1618 "optional": cty.NullVal(cty.String), // config is null 1619 "computed": cty.StringVal("hello"), // computed values come from prior 1620 "optional_computed": cty.StringVal("hello"), // config takes precedent over prior in opt+computed 1621 "required": cty.StringVal("present"), // value from config 1622 }), 1623 }), 1624 }), 1625 }, 1626 "deeply nested set": { 1627 &configschema.Block{ 1628 Attributes: map[string]*configschema.Attribute{ 1629 "foo": { 1630 NestedType: &configschema.Object{ 1631 Nesting: configschema.NestingSet, 1632 Attributes: map[string]*configschema.Attribute{ 1633 "bar": { 1634 NestedType: &configschema.Object{ 1635 Nesting: configschema.NestingSet, 1636 Attributes: testAttributes, 1637 }, 1638 Required: true, 1639 }, 1640 }, 1641 }, 1642 Optional: true, 1643 }, 1644 }, 1645 }, 1646 // prior values 1647 cty.ObjectVal(map[string]cty.Value{ 1648 "foo": cty.SetVal([]cty.Value{ 1649 cty.ObjectVal(map[string]cty.Value{ 1650 "bar": cty.SetVal([]cty.Value{ 1651 cty.ObjectVal(map[string]cty.Value{ 1652 "optional": cty.StringVal("prior"), 1653 "computed": cty.StringVal("prior"), 1654 "optional_computed": cty.StringVal("prior"), 1655 "required": cty.StringVal("prior"), 1656 }), 1657 }), 1658 }), 1659 cty.ObjectVal(map[string]cty.Value{ 1660 "bar": cty.SetVal([]cty.Value{ 1661 cty.ObjectVal(map[string]cty.Value{ 1662 "optional": cty.StringVal("other_prior"), 1663 "computed": cty.StringVal("other_prior"), 1664 "optional_computed": cty.StringVal("other_prior"), 1665 "required": cty.StringVal("other_prior"), 1666 }), 1667 }), 1668 }), 1669 }), 1670 }), 1671 // config differs from prior 1672 cty.ObjectVal(map[string]cty.Value{ 1673 "foo": cty.SetVal([]cty.Value{ 1674 cty.ObjectVal(map[string]cty.Value{ 1675 "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 1676 "optional": cty.StringVal("configured"), 1677 "computed": cty.NullVal(cty.String), // computed attrs are null in config 1678 "optional_computed": cty.StringVal("configured"), 1679 "required": cty.StringVal("configured"), 1680 })}), 1681 }), 1682 cty.ObjectVal(map[string]cty.Value{ 1683 "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 1684 "optional": cty.NullVal(cty.String), // explicit null in config 1685 "computed": cty.NullVal(cty.String), // computed attrs are null in config 1686 "optional_computed": cty.StringVal("other_configured"), 1687 "required": cty.StringVal("other_configured"), 1688 })}), 1689 }), 1690 }), 1691 }), 1692 // want: 1693 cty.ObjectVal(map[string]cty.Value{ 1694 "foo": cty.SetVal([]cty.Value{ 1695 cty.ObjectVal(map[string]cty.Value{ 1696 "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 1697 "optional": cty.StringVal("configured"), 1698 "computed": cty.NullVal(cty.String), 1699 "optional_computed": cty.StringVal("configured"), 1700 "required": cty.StringVal("configured"), 1701 })}), 1702 }), 1703 cty.ObjectVal(map[string]cty.Value{ 1704 "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 1705 "optional": cty.NullVal(cty.String), // explicit null in config is preserved 1706 "computed": cty.NullVal(cty.String), 1707 "optional_computed": cty.StringVal("other_configured"), 1708 "required": cty.StringVal("other_configured"), 1709 })}), 1710 }), 1711 }), 1712 }), 1713 }, 1714 "expected null NestedTypes": { 1715 &configschema.Block{ 1716 Attributes: map[string]*configschema.Attribute{ 1717 "single": { 1718 NestedType: &configschema.Object{ 1719 Nesting: configschema.NestingSingle, 1720 Attributes: map[string]*configschema.Attribute{ 1721 "bar": {Type: cty.String}, 1722 }, 1723 }, 1724 Optional: true, 1725 }, 1726 "list": { 1727 NestedType: &configschema.Object{ 1728 Nesting: configschema.NestingList, 1729 Attributes: map[string]*configschema.Attribute{ 1730 "bar": {Type: cty.String}, 1731 }, 1732 }, 1733 Optional: true, 1734 }, 1735 "set": { 1736 NestedType: &configschema.Object{ 1737 Nesting: configschema.NestingSet, 1738 Attributes: map[string]*configschema.Attribute{ 1739 "bar": {Type: cty.String}, 1740 }, 1741 }, 1742 Optional: true, 1743 }, 1744 "map": { 1745 NestedType: &configschema.Object{ 1746 Nesting: configschema.NestingMap, 1747 Attributes: map[string]*configschema.Attribute{ 1748 "bar": {Type: cty.String}, 1749 }, 1750 }, 1751 Optional: true, 1752 }, 1753 "nested_map": { 1754 NestedType: &configschema.Object{ 1755 Nesting: configschema.NestingMap, 1756 Attributes: map[string]*configschema.Attribute{ 1757 "inner": { 1758 NestedType: &configschema.Object{ 1759 Nesting: configschema.NestingSingle, 1760 Attributes: testAttributes, 1761 }, 1762 }, 1763 }, 1764 }, 1765 Optional: true, 1766 }, 1767 }, 1768 }, 1769 cty.ObjectVal(map[string]cty.Value{ 1770 "single": cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}), 1771 "list": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")})}), 1772 "map": cty.MapVal(map[string]cty.Value{ 1773 "map_entry": cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}), 1774 }), 1775 "set": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")})}), 1776 "nested_map": cty.MapVal(map[string]cty.Value{ 1777 "a": cty.ObjectVal(map[string]cty.Value{ 1778 "inner": cty.ObjectVal(map[string]cty.Value{ 1779 "optional": cty.StringVal("foo"), 1780 "computed": cty.StringVal("foo"), 1781 "optional_computed": cty.StringVal("foo"), 1782 "required": cty.StringVal("foo"), 1783 }), 1784 }), 1785 }), 1786 }), 1787 cty.ObjectVal(map[string]cty.Value{ 1788 "single": cty.NullVal(cty.Object(map[string]cty.Type{"bar": cty.String})), 1789 "list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1790 "map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1791 "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1792 "nested_map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ 1793 "inner": cty.Object(map[string]cty.Type{ 1794 "optional": cty.String, 1795 "computed": cty.String, 1796 "optional_computed": cty.String, 1797 "required": cty.String, 1798 }), 1799 }))), 1800 }), 1801 cty.ObjectVal(map[string]cty.Value{ 1802 "single": cty.NullVal(cty.Object(map[string]cty.Type{"bar": cty.String})), 1803 "list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1804 "map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1805 "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1806 "nested_map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ 1807 "inner": cty.Object(map[string]cty.Type{ 1808 "optional": cty.String, 1809 "computed": cty.String, 1810 "optional_computed": cty.String, 1811 "required": cty.String, 1812 }), 1813 }))), 1814 }), 1815 }, 1816 "expected empty NestedTypes": { 1817 &configschema.Block{ 1818 Attributes: map[string]*configschema.Attribute{ 1819 "set": { 1820 NestedType: &configschema.Object{ 1821 Nesting: configschema.NestingSet, 1822 Attributes: map[string]*configschema.Attribute{ 1823 "bar": {Type: cty.String}, 1824 }, 1825 }, 1826 Optional: true, 1827 }, 1828 "map": { 1829 NestedType: &configschema.Object{ 1830 Nesting: configschema.NestingMap, 1831 Attributes: map[string]*configschema.Attribute{ 1832 "bar": {Type: cty.String}, 1833 }, 1834 }, 1835 Optional: true, 1836 }, 1837 }, 1838 }, 1839 cty.ObjectVal(map[string]cty.Value{ 1840 "map": cty.MapValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1841 "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1842 }), 1843 cty.ObjectVal(map[string]cty.Value{ 1844 "map": cty.MapValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1845 "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1846 }), 1847 cty.ObjectVal(map[string]cty.Value{ 1848 "map": cty.MapValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1849 "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1850 }), 1851 }, 1852 "optional types set replacement": { 1853 &configschema.Block{ 1854 Attributes: map[string]*configschema.Attribute{ 1855 "set": { 1856 NestedType: &configschema.Object{ 1857 Nesting: configschema.NestingSet, 1858 Attributes: map[string]*configschema.Attribute{ 1859 "bar": { 1860 Type: cty.String, 1861 Required: true, 1862 }, 1863 }, 1864 }, 1865 Optional: true, 1866 }, 1867 }, 1868 }, 1869 cty.ObjectVal(map[string]cty.Value{ 1870 "set": cty.SetVal([]cty.Value{ 1871 cty.ObjectVal(map[string]cty.Value{ 1872 "bar": cty.StringVal("old"), 1873 }), 1874 }), 1875 }), 1876 cty.ObjectVal(map[string]cty.Value{ 1877 "set": cty.SetVal([]cty.Value{ 1878 cty.ObjectVal(map[string]cty.Value{ 1879 "bar": cty.StringVal("new"), 1880 }), 1881 }), 1882 }), 1883 cty.ObjectVal(map[string]cty.Value{ 1884 "set": cty.SetVal([]cty.Value{ 1885 cty.ObjectVal(map[string]cty.Value{ 1886 "bar": cty.StringVal("new"), 1887 }), 1888 }), 1889 }), 1890 }, 1891 "prior null nested objects": { 1892 &configschema.Block{ 1893 Attributes: map[string]*configschema.Attribute{ 1894 "single": { 1895 NestedType: &configschema.Object{ 1896 Nesting: configschema.NestingSingle, 1897 Attributes: map[string]*configschema.Attribute{ 1898 "list": { 1899 NestedType: &configschema.Object{ 1900 Nesting: configschema.NestingList, 1901 Attributes: map[string]*configschema.Attribute{ 1902 "foo": { 1903 Type: cty.String, 1904 }, 1905 }, 1906 }, 1907 Optional: true, 1908 }, 1909 }, 1910 }, 1911 Optional: true, 1912 }, 1913 "map": { 1914 NestedType: &configschema.Object{ 1915 Nesting: configschema.NestingMap, 1916 Attributes: map[string]*configschema.Attribute{ 1917 "map": { 1918 NestedType: &configschema.Object{ 1919 Nesting: configschema.NestingList, 1920 Attributes: map[string]*configschema.Attribute{ 1921 "foo": { 1922 Type: cty.String, 1923 }, 1924 }, 1925 }, 1926 Optional: true, 1927 }, 1928 }, 1929 }, 1930 Optional: true, 1931 }, 1932 }, 1933 }, 1934 cty.NullVal(cty.Object(map[string]cty.Type{ 1935 "single": cty.Object(map[string]cty.Type{ 1936 "list": cty.List(cty.Object(map[string]cty.Type{ 1937 "foo": cty.String, 1938 })), 1939 }), 1940 "map": cty.Map(cty.Object(map[string]cty.Type{ 1941 "list": cty.List(cty.Object(map[string]cty.Type{ 1942 "foo": cty.String, 1943 })), 1944 })), 1945 })), 1946 cty.ObjectVal(map[string]cty.Value{ 1947 "single": cty.ObjectVal(map[string]cty.Value{ 1948 "list": cty.ListVal([]cty.Value{ 1949 cty.ObjectVal(map[string]cty.Value{ 1950 "foo": cty.StringVal("a"), 1951 }), 1952 cty.ObjectVal(map[string]cty.Value{ 1953 "foo": cty.StringVal("b"), 1954 }), 1955 }), 1956 }), 1957 "map": cty.MapVal(map[string]cty.Value{ 1958 "one": cty.ObjectVal(map[string]cty.Value{ 1959 "list": cty.ListVal([]cty.Value{ 1960 cty.ObjectVal(map[string]cty.Value{ 1961 "foo": cty.StringVal("a"), 1962 }), 1963 cty.ObjectVal(map[string]cty.Value{ 1964 "foo": cty.StringVal("b"), 1965 }), 1966 }), 1967 }), 1968 }), 1969 }), 1970 cty.ObjectVal(map[string]cty.Value{ 1971 "single": cty.ObjectVal(map[string]cty.Value{ 1972 "list": cty.ListVal([]cty.Value{ 1973 cty.ObjectVal(map[string]cty.Value{ 1974 "foo": cty.StringVal("a"), 1975 }), 1976 cty.ObjectVal(map[string]cty.Value{ 1977 "foo": cty.StringVal("b"), 1978 }), 1979 }), 1980 }), 1981 "map": cty.MapVal(map[string]cty.Value{ 1982 "one": cty.ObjectVal(map[string]cty.Value{ 1983 "list": cty.ListVal([]cty.Value{ 1984 cty.ObjectVal(map[string]cty.Value{ 1985 "foo": cty.StringVal("a"), 1986 }), 1987 cty.ObjectVal(map[string]cty.Value{ 1988 "foo": cty.StringVal("b"), 1989 }), 1990 }), 1991 }), 1992 }), 1993 }), 1994 }, 1995 1996 // Data sources are planned with an unknown value. 1997 // Note that this plan fails AssertPlanValid, because for managed 1998 // resources an instance would never be completely unknown. 1999 "unknown prior nested objects": { 2000 &configschema.Block{ 2001 Attributes: map[string]*configschema.Attribute{ 2002 "list": { 2003 NestedType: &configschema.Object{ 2004 Nesting: configschema.NestingList, 2005 Attributes: map[string]*configschema.Attribute{ 2006 "list": { 2007 NestedType: &configschema.Object{ 2008 Nesting: configschema.NestingList, 2009 Attributes: map[string]*configschema.Attribute{ 2010 "foo": { 2011 Type: cty.String, 2012 }, 2013 }, 2014 }, 2015 Computed: true, 2016 }, 2017 }, 2018 }, 2019 Computed: true, 2020 }, 2021 }, 2022 }, 2023 cty.UnknownVal(cty.Object(map[string]cty.Type{ 2024 "list": cty.List(cty.Object(map[string]cty.Type{ 2025 "list": cty.List(cty.Object(map[string]cty.Type{ 2026 "foo": cty.String, 2027 })), 2028 })), 2029 })), 2030 cty.NullVal(cty.Object(map[string]cty.Type{ 2031 "list": cty.List(cty.Object(map[string]cty.Type{ 2032 "list": cty.List(cty.Object(map[string]cty.Type{ 2033 "foo": cty.String, 2034 })), 2035 })), 2036 })), 2037 cty.UnknownVal(cty.Object(map[string]cty.Type{ 2038 "list": cty.List(cty.Object(map[string]cty.Type{ 2039 "list": cty.List(cty.Object(map[string]cty.Type{ 2040 "foo": cty.String, 2041 })), 2042 })), 2043 })), 2044 }, 2045 2046 // A nested object with computed attributes, which is contained in an 2047 // optional+computed container. The nested computed values should be 2048 // represented in the proposed new object. 2049 "config within optional+computed": { 2050 &configschema.Block{ 2051 Attributes: map[string]*configschema.Attribute{ 2052 "list_obj": { 2053 Optional: true, 2054 Computed: true, 2055 NestedType: &configschema.Object{ 2056 Nesting: configschema.NestingList, 2057 Attributes: map[string]*configschema.Attribute{ 2058 "obj": { 2059 Optional: true, 2060 NestedType: &configschema.Object{ 2061 Nesting: configschema.NestingSingle, 2062 Attributes: map[string]*configschema.Attribute{ 2063 "optional": {Type: cty.String, Optional: true}, 2064 "computed": {Type: cty.String, Computed: true}, 2065 }, 2066 }, 2067 }, 2068 }, 2069 }, 2070 }, 2071 }, 2072 }, 2073 cty.ObjectVal(map[string]cty.Value{ 2074 "list_obj": cty.ListVal([]cty.Value{ 2075 cty.ObjectVal(map[string]cty.Value{ 2076 "obj": cty.ObjectVal(map[string]cty.Value{ 2077 "optional": cty.StringVal("prior"), 2078 "computed": cty.StringVal("prior computed"), 2079 }), 2080 }), 2081 }), 2082 }), 2083 cty.ObjectVal(map[string]cty.Value{ 2084 "list_obj": cty.ListVal([]cty.Value{ 2085 cty.ObjectVal(map[string]cty.Value{ 2086 "obj": cty.ObjectVal(map[string]cty.Value{ 2087 "optional": cty.StringVal("prior"), 2088 "computed": cty.NullVal(cty.String), 2089 }), 2090 }), 2091 }), 2092 }), 2093 cty.ObjectVal(map[string]cty.Value{ 2094 "list_obj": cty.ListVal([]cty.Value{ 2095 cty.ObjectVal(map[string]cty.Value{ 2096 "obj": cty.ObjectVal(map[string]cty.Value{ 2097 "optional": cty.StringVal("prior"), 2098 "computed": cty.StringVal("prior computed"), 2099 }), 2100 }), 2101 }), 2102 }), 2103 }, 2104 2105 // A nested object with computed attributes, which is contained in an 2106 // optional+computed container. The prior nested object contains values 2107 // which could not be computed, therefor the proposed new value must be 2108 // the null value from the configuration. 2109 "computed within optional+computed": { 2110 &configschema.Block{ 2111 Attributes: map[string]*configschema.Attribute{ 2112 "list_obj": { 2113 Optional: true, 2114 Computed: true, 2115 NestedType: &configschema.Object{ 2116 Nesting: configschema.NestingList, 2117 Attributes: map[string]*configschema.Attribute{ 2118 "obj": { 2119 Optional: true, 2120 NestedType: &configschema.Object{ 2121 Nesting: configschema.NestingSingle, 2122 Attributes: map[string]*configschema.Attribute{ 2123 "optional": {Type: cty.String, Optional: true}, 2124 "computed": {Type: cty.String, Computed: true}, 2125 }, 2126 }, 2127 }, 2128 }, 2129 }, 2130 }, 2131 }, 2132 }, 2133 cty.ObjectVal(map[string]cty.Value{ 2134 "list_obj": cty.ListVal([]cty.Value{ 2135 cty.ObjectVal(map[string]cty.Value{ 2136 "obj": cty.ObjectVal(map[string]cty.Value{ 2137 "optional": cty.StringVal("prior"), 2138 "computed": cty.StringVal("prior computed"), 2139 }), 2140 }), 2141 }), 2142 }), 2143 cty.ObjectVal(map[string]cty.Value{ 2144 "list_obj": cty.NullVal(cty.List( 2145 cty.Object(map[string]cty.Type{ 2146 "obj": cty.Object(map[string]cty.Type{ 2147 "optional": cty.String, 2148 "computed": cty.String, 2149 }), 2150 }), 2151 )), 2152 }), 2153 cty.ObjectVal(map[string]cty.Value{ 2154 "list_obj": cty.NullVal(cty.List( 2155 cty.Object(map[string]cty.Type{ 2156 "obj": cty.Object(map[string]cty.Type{ 2157 "optional": cty.String, 2158 "computed": cty.String, 2159 }), 2160 }), 2161 )), 2162 }), 2163 }, 2164 2165 // A nested object with computed attributes, which is contained in an 2166 // optional+computed set. The nested computed values should be 2167 // represented in the proposed new object, and correlated with state 2168 // via the non-computed attributes. 2169 "config add within optional+computed set": { 2170 &configschema.Block{ 2171 Attributes: map[string]*configschema.Attribute{ 2172 "set_obj": { 2173 Optional: true, 2174 Computed: true, 2175 NestedType: &configschema.Object{ 2176 Nesting: configschema.NestingSet, 2177 Attributes: map[string]*configschema.Attribute{ 2178 "obj": { 2179 Optional: true, 2180 NestedType: &configschema.Object{ 2181 Nesting: configschema.NestingSingle, 2182 Attributes: map[string]*configschema.Attribute{ 2183 "optional": {Type: cty.String, Optional: true}, 2184 "computed": {Type: cty.String, Computed: true}, 2185 }, 2186 }, 2187 }, 2188 }, 2189 }, 2190 }, 2191 }, 2192 }, 2193 cty.ObjectVal(map[string]cty.Value{ 2194 "set_obj": cty.SetVal([]cty.Value{ 2195 cty.ObjectVal(map[string]cty.Value{ 2196 "obj": cty.ObjectVal(map[string]cty.Value{ 2197 "optional": cty.StringVal("first"), 2198 "computed": cty.StringVal("first computed"), 2199 }), 2200 }), 2201 cty.ObjectVal(map[string]cty.Value{ 2202 "obj": cty.ObjectVal(map[string]cty.Value{ 2203 "optional": cty.StringVal("second"), 2204 "computed": cty.StringVal("second computed"), 2205 }), 2206 }), 2207 }), 2208 }), 2209 cty.ObjectVal(map[string]cty.Value{ 2210 "set_obj": cty.SetVal([]cty.Value{ 2211 cty.ObjectVal(map[string]cty.Value{ 2212 "obj": cty.ObjectVal(map[string]cty.Value{ 2213 "optional": cty.StringVal("first"), 2214 "computed": cty.NullVal(cty.String), 2215 }), 2216 }), 2217 cty.ObjectVal(map[string]cty.Value{ 2218 "obj": cty.ObjectVal(map[string]cty.Value{ 2219 "optional": cty.StringVal("second"), 2220 "computed": cty.NullVal(cty.String), 2221 }), 2222 }), 2223 cty.ObjectVal(map[string]cty.Value{ 2224 "obj": cty.ObjectVal(map[string]cty.Value{ 2225 "optional": cty.StringVal("third"), 2226 "computed": cty.NullVal(cty.String), 2227 }), 2228 }), 2229 }), 2230 }), 2231 cty.ObjectVal(map[string]cty.Value{ 2232 "set_obj": cty.SetVal([]cty.Value{ 2233 cty.ObjectVal(map[string]cty.Value{ 2234 "obj": cty.ObjectVal(map[string]cty.Value{ 2235 "optional": cty.StringVal("first"), 2236 "computed": cty.StringVal("first computed"), 2237 }), 2238 }), 2239 cty.ObjectVal(map[string]cty.Value{ 2240 "obj": cty.ObjectVal(map[string]cty.Value{ 2241 "optional": cty.StringVal("second"), 2242 "computed": cty.StringVal("second computed"), 2243 }), 2244 }), 2245 cty.ObjectVal(map[string]cty.Value{ 2246 "obj": cty.ObjectVal(map[string]cty.Value{ 2247 "optional": cty.StringVal("third"), 2248 "computed": cty.NullVal(cty.String), 2249 }), 2250 }), 2251 }), 2252 }), 2253 }, 2254 2255 // A nested object with computed attributes, which is contained in a 2256 // set. The nested computed values should be represented in the 2257 // proposed new object, and correlated with state via the non-computed 2258 // attributes. 2259 "config add within set block": { 2260 &configschema.Block{ 2261 BlockTypes: map[string]*configschema.NestedBlock{ 2262 "set_obj": { 2263 Nesting: configschema.NestingSet, 2264 Block: configschema.Block{ 2265 Attributes: map[string]*configschema.Attribute{ 2266 "obj": { 2267 Optional: true, 2268 NestedType: &configschema.Object{ 2269 Nesting: configschema.NestingSingle, 2270 Attributes: map[string]*configschema.Attribute{ 2271 "optional": {Type: cty.String, Optional: true}, 2272 "computed": {Type: cty.String, Optional: true, Computed: true}, 2273 }, 2274 }, 2275 }, 2276 }, 2277 }, 2278 }, 2279 }, 2280 }, 2281 cty.ObjectVal(map[string]cty.Value{ 2282 "set_obj": cty.SetVal([]cty.Value{ 2283 cty.ObjectVal(map[string]cty.Value{ 2284 "obj": cty.ObjectVal(map[string]cty.Value{ 2285 "optional": cty.StringVal("first"), 2286 "computed": cty.StringVal("first computed"), 2287 }), 2288 }), 2289 cty.ObjectVal(map[string]cty.Value{ 2290 "obj": cty.ObjectVal(map[string]cty.Value{ 2291 "optional": cty.StringVal("second"), 2292 "computed": cty.StringVal("second from config"), 2293 }), 2294 }), 2295 }), 2296 }), 2297 cty.ObjectVal(map[string]cty.Value{ 2298 "set_obj": cty.SetVal([]cty.Value{ 2299 cty.ObjectVal(map[string]cty.Value{ 2300 "obj": cty.ObjectVal(map[string]cty.Value{ 2301 "optional": cty.StringVal("first"), 2302 "computed": cty.NullVal(cty.String), 2303 }), 2304 }), 2305 cty.ObjectVal(map[string]cty.Value{ 2306 "obj": cty.ObjectVal(map[string]cty.Value{ 2307 "optional": cty.StringVal("second"), 2308 "computed": cty.StringVal("second from config"), 2309 }), 2310 }), 2311 // new "third" value added 2312 cty.ObjectVal(map[string]cty.Value{ 2313 "obj": cty.ObjectVal(map[string]cty.Value{ 2314 "optional": cty.StringVal("third"), 2315 "computed": cty.NullVal(cty.String), 2316 }), 2317 }), 2318 }), 2319 }), 2320 cty.ObjectVal(map[string]cty.Value{ 2321 "set_obj": cty.SetVal([]cty.Value{ 2322 cty.ObjectVal(map[string]cty.Value{ 2323 "obj": cty.ObjectVal(map[string]cty.Value{ 2324 "optional": cty.StringVal("first"), 2325 "computed": cty.StringVal("first computed"), 2326 }), 2327 }), 2328 cty.ObjectVal(map[string]cty.Value{ 2329 "obj": cty.ObjectVal(map[string]cty.Value{ 2330 "optional": cty.StringVal("second"), 2331 "computed": cty.StringVal("second from config"), 2332 }), 2333 }), 2334 cty.ObjectVal(map[string]cty.Value{ 2335 "obj": cty.ObjectVal(map[string]cty.Value{ 2336 "optional": cty.StringVal("third"), 2337 "computed": cty.NullVal(cty.String), 2338 }), 2339 }), 2340 }), 2341 }), 2342 }, 2343 2344 // A nested object with computed attributes, which is contained in a 2345 // set. The nested computed values should be represented in the 2346 // proposed new object, and correlated with state via the non-computed 2347 // attributes. 2348 "config change within set block": { 2349 &configschema.Block{ 2350 BlockTypes: map[string]*configschema.NestedBlock{ 2351 "set_obj": { 2352 Nesting: configschema.NestingSet, 2353 Block: configschema.Block{ 2354 Attributes: map[string]*configschema.Attribute{ 2355 "obj": { 2356 Optional: true, 2357 NestedType: &configschema.Object{ 2358 Nesting: configschema.NestingSingle, 2359 Attributes: map[string]*configschema.Attribute{ 2360 "optional": {Type: cty.String, Optional: true}, 2361 "computed": {Type: cty.String, Optional: true, Computed: true}, 2362 }, 2363 }, 2364 }, 2365 }, 2366 }, 2367 }, 2368 }, 2369 }, 2370 cty.ObjectVal(map[string]cty.Value{ 2371 "set_obj": cty.SetVal([]cty.Value{ 2372 cty.ObjectVal(map[string]cty.Value{ 2373 "obj": cty.ObjectVal(map[string]cty.Value{ 2374 "optional": cty.StringVal("first"), 2375 "computed": cty.StringVal("first computed"), 2376 }), 2377 }), 2378 cty.ObjectVal(map[string]cty.Value{ 2379 "obj": cty.ObjectVal(map[string]cty.Value{ 2380 "optional": cty.StringVal("second"), 2381 "computed": cty.StringVal("second computed"), 2382 }), 2383 }), 2384 }), 2385 }), 2386 cty.ObjectVal(map[string]cty.Value{ 2387 "set_obj": cty.SetVal([]cty.Value{ 2388 cty.ObjectVal(map[string]cty.Value{ 2389 "obj": cty.ObjectVal(map[string]cty.Value{ 2390 "optional": cty.StringVal("first"), 2391 "computed": cty.NullVal(cty.String), 2392 }), 2393 }), 2394 cty.ObjectVal(map[string]cty.Value{ 2395 "obj": cty.ObjectVal(map[string]cty.Value{ 2396 "optional": cty.StringVal("changed"), 2397 "computed": cty.NullVal(cty.String), 2398 }), 2399 }), 2400 }), 2401 }), 2402 cty.ObjectVal(map[string]cty.Value{ 2403 "set_obj": cty.SetVal([]cty.Value{ 2404 cty.ObjectVal(map[string]cty.Value{ 2405 "obj": cty.ObjectVal(map[string]cty.Value{ 2406 "optional": cty.StringVal("first"), 2407 "computed": cty.StringVal("first computed"), 2408 }), 2409 }), 2410 cty.ObjectVal(map[string]cty.Value{ 2411 "obj": cty.ObjectVal(map[string]cty.Value{ 2412 "optional": cty.StringVal("changed"), 2413 "computed": cty.NullVal(cty.String), 2414 }), 2415 }), 2416 }), 2417 }), 2418 }, 2419 2420 "set attr with partial optional computed change": { 2421 &configschema.Block{ 2422 Attributes: map[string]*configschema.Attribute{ 2423 "multi": { 2424 Optional: true, 2425 NestedType: &configschema.Object{ 2426 Nesting: configschema.NestingSet, 2427 Attributes: map[string]*configschema.Attribute{ 2428 "opt": { 2429 Type: cty.String, 2430 Optional: true, 2431 }, 2432 "oc": { 2433 Type: cty.String, 2434 Optional: true, 2435 Computed: true, 2436 }, 2437 }, 2438 }, 2439 }, 2440 }, 2441 }, 2442 cty.ObjectVal(map[string]cty.Value{ 2443 "multi": cty.SetVal([]cty.Value{ 2444 cty.ObjectVal(map[string]cty.Value{ 2445 "opt": cty.StringVal("one"), 2446 "oc": cty.StringVal("OK"), 2447 }), 2448 cty.ObjectVal(map[string]cty.Value{ 2449 "opt": cty.StringVal("two"), 2450 "oc": cty.StringVal("OK"), 2451 }), 2452 }), 2453 }), 2454 cty.ObjectVal(map[string]cty.Value{ 2455 "multi": cty.SetVal([]cty.Value{ 2456 cty.ObjectVal(map[string]cty.Value{ 2457 "opt": cty.StringVal("one"), 2458 "oc": cty.NullVal(cty.String), 2459 }), 2460 cty.ObjectVal(map[string]cty.Value{ 2461 "opt": cty.StringVal("replaced"), 2462 "oc": cty.NullVal(cty.String), 2463 }), 2464 }), 2465 }), 2466 cty.ObjectVal(map[string]cty.Value{ 2467 "multi": cty.SetVal([]cty.Value{ 2468 cty.ObjectVal(map[string]cty.Value{ 2469 "opt": cty.StringVal("one"), 2470 "oc": cty.StringVal("OK"), 2471 }), 2472 cty.ObjectVal(map[string]cty.Value{ 2473 "opt": cty.StringVal("replaced"), 2474 "oc": cty.NullVal(cty.String), 2475 }), 2476 }), 2477 }), 2478 }, 2479 2480 "set attr without optional computed change": { 2481 &configschema.Block{ 2482 Attributes: map[string]*configschema.Attribute{ 2483 "multi": { 2484 Optional: true, 2485 NestedType: &configschema.Object{ 2486 Nesting: configschema.NestingSet, 2487 Attributes: map[string]*configschema.Attribute{ 2488 "opt": { 2489 Type: cty.String, 2490 Optional: true, 2491 }, 2492 "oc": { 2493 Type: cty.String, 2494 Optional: true, 2495 Computed: true, 2496 }, 2497 }, 2498 }, 2499 }, 2500 }, 2501 }, 2502 cty.ObjectVal(map[string]cty.Value{ 2503 "multi": cty.SetVal([]cty.Value{ 2504 cty.ObjectVal(map[string]cty.Value{ 2505 "opt": cty.StringVal("one"), 2506 "oc": cty.StringVal("OK"), 2507 }), 2508 cty.ObjectVal(map[string]cty.Value{ 2509 "opt": cty.StringVal("two"), 2510 "oc": cty.StringVal("OK"), 2511 }), 2512 }), 2513 }), 2514 cty.ObjectVal(map[string]cty.Value{ 2515 "multi": cty.SetVal([]cty.Value{ 2516 cty.ObjectVal(map[string]cty.Value{ 2517 "opt": cty.StringVal("one"), 2518 "oc": cty.NullVal(cty.String), 2519 }), 2520 cty.ObjectVal(map[string]cty.Value{ 2521 "opt": cty.StringVal("two"), 2522 "oc": cty.NullVal(cty.String), 2523 }), 2524 }), 2525 }), 2526 cty.ObjectVal(map[string]cty.Value{ 2527 "multi": cty.SetVal([]cty.Value{ 2528 cty.ObjectVal(map[string]cty.Value{ 2529 "opt": cty.StringVal("one"), 2530 "oc": cty.StringVal("OK"), 2531 }), 2532 cty.ObjectVal(map[string]cty.Value{ 2533 "opt": cty.StringVal("two"), 2534 "oc": cty.StringVal("OK"), 2535 }), 2536 }), 2537 }), 2538 }, 2539 2540 "set attr with all optional computed": { 2541 &configschema.Block{ 2542 Attributes: map[string]*configschema.Attribute{ 2543 "multi": { 2544 Optional: true, 2545 NestedType: &configschema.Object{ 2546 Nesting: configschema.NestingSet, 2547 Attributes: map[string]*configschema.Attribute{ 2548 "opt": { 2549 Type: cty.String, 2550 Optional: true, 2551 Computed: true, 2552 }, 2553 "oc": { 2554 Type: cty.String, 2555 Optional: true, 2556 Computed: true, 2557 }, 2558 }, 2559 }, 2560 }, 2561 }, 2562 }, 2563 cty.ObjectVal(map[string]cty.Value{ 2564 "multi": cty.SetVal([]cty.Value{ 2565 cty.ObjectVal(map[string]cty.Value{ 2566 "opt": cty.StringVal("one"), 2567 "oc": cty.StringVal("OK"), 2568 }), 2569 cty.ObjectVal(map[string]cty.Value{ 2570 "opt": cty.StringVal("two"), 2571 "oc": cty.StringVal("OK"), 2572 }), 2573 }), 2574 }), 2575 // Each of these values can be correlated by the existence of the 2576 // optional config attribute. Because "one" and "two" are set in 2577 // the config, they must exist in the state regardless of 2578 // optional&computed. 2579 cty.ObjectVal(map[string]cty.Value{ 2580 "multi": cty.SetVal([]cty.Value{ 2581 cty.ObjectVal(map[string]cty.Value{ 2582 "opt": cty.StringVal("one"), 2583 "oc": cty.NullVal(cty.String), 2584 }), 2585 cty.ObjectVal(map[string]cty.Value{ 2586 "opt": cty.StringVal("two"), 2587 "oc": cty.NullVal(cty.String), 2588 }), 2589 }), 2590 }), 2591 cty.ObjectVal(map[string]cty.Value{ 2592 "multi": cty.SetVal([]cty.Value{ 2593 cty.ObjectVal(map[string]cty.Value{ 2594 "opt": cty.StringVal("one"), 2595 "oc": cty.StringVal("OK"), 2596 }), 2597 cty.ObjectVal(map[string]cty.Value{ 2598 "opt": cty.StringVal("two"), 2599 "oc": cty.StringVal("OK"), 2600 }), 2601 }), 2602 }), 2603 }, 2604 2605 "set block with all optional computed and nested object types": { 2606 &configschema.Block{ 2607 BlockTypes: map[string]*configschema.NestedBlock{ 2608 "multi": { 2609 Nesting: configschema.NestingSet, 2610 Block: configschema.Block{ 2611 Attributes: map[string]*configschema.Attribute{ 2612 "opt": { 2613 Type: cty.String, 2614 Optional: true, 2615 Computed: true, 2616 }, 2617 "oc": { 2618 Type: cty.String, 2619 Optional: true, 2620 Computed: true, 2621 }, 2622 "attr": { 2623 Optional: true, 2624 NestedType: &configschema.Object{ 2625 Nesting: configschema.NestingSet, 2626 Attributes: map[string]*configschema.Attribute{ 2627 "opt": { 2628 Type: cty.String, 2629 Optional: true, 2630 Computed: true, 2631 }, 2632 "oc": { 2633 Type: cty.String, 2634 Optional: true, 2635 Computed: true, 2636 }, 2637 }, 2638 }, 2639 }, 2640 }, 2641 }, 2642 }, 2643 }, 2644 }, 2645 cty.ObjectVal(map[string]cty.Value{ 2646 "multi": cty.SetVal([]cty.Value{ 2647 cty.ObjectVal(map[string]cty.Value{ 2648 "opt": cty.StringVal("one"), 2649 "oc": cty.StringVal("OK"), 2650 "attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 2651 "opt": cty.StringVal("one"), 2652 "oc": cty.StringVal("OK"), 2653 })}), 2654 }), 2655 cty.ObjectVal(map[string]cty.Value{ 2656 "opt": cty.StringVal("two"), 2657 "oc": cty.StringVal("OK"), 2658 "attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 2659 "opt": cty.StringVal("two"), 2660 "oc": cty.StringVal("OK"), 2661 })}), 2662 }), 2663 }), 2664 }), 2665 cty.ObjectVal(map[string]cty.Value{ 2666 "multi": cty.SetVal([]cty.Value{ 2667 cty.ObjectVal(map[string]cty.Value{ 2668 "opt": cty.StringVal("one"), 2669 "oc": cty.NullVal(cty.String), 2670 "attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 2671 "opt": cty.StringVal("one"), 2672 "oc": cty.StringVal("OK"), 2673 })}), 2674 }), 2675 cty.ObjectVal(map[string]cty.Value{ 2676 "opt": cty.StringVal("two"), 2677 "oc": cty.StringVal("OK"), 2678 "attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 2679 "opt": cty.StringVal("two"), 2680 "oc": cty.NullVal(cty.String), 2681 })}), 2682 }), 2683 cty.ObjectVal(map[string]cty.Value{ 2684 "opt": cty.StringVal("three"), 2685 "oc": cty.NullVal(cty.String), 2686 "attr": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ 2687 "opt": cty.String, 2688 "oc": cty.String, 2689 }))), 2690 }), 2691 }), 2692 }), 2693 cty.ObjectVal(map[string]cty.Value{ 2694 "multi": cty.SetVal([]cty.Value{ 2695 // We can correlate this with prior from the outer object 2696 // attributes, and the equal nested set. 2697 cty.ObjectVal(map[string]cty.Value{ 2698 "opt": cty.StringVal("one"), 2699 "oc": cty.StringVal("OK"), 2700 "attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 2701 "opt": cty.StringVal("one"), 2702 "oc": cty.StringVal("OK"), 2703 })}), 2704 }), 2705 // This value is overridden by config, because we can't 2706 // correlate optional+computed config values within nested 2707 // sets. 2708 cty.ObjectVal(map[string]cty.Value{ 2709 "opt": cty.StringVal("two"), 2710 "oc": cty.StringVal("OK"), 2711 "attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 2712 "opt": cty.StringVal("two"), 2713 "oc": cty.NullVal(cty.String), 2714 })}), 2715 }), 2716 // This value was taken only from config 2717 cty.ObjectVal(map[string]cty.Value{ 2718 "opt": cty.StringVal("three"), 2719 "oc": cty.NullVal(cty.String), 2720 "attr": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ 2721 "opt": cty.String, 2722 "oc": cty.String, 2723 }))), 2724 }), 2725 }), 2726 }), 2727 }, 2728 } 2729 2730 for name, test := range tests { 2731 t.Run(name, func(t *testing.T) { 2732 got := ProposedNew(test.Schema, test.Prior, test.Config) 2733 if !got.RawEquals(test.Want) { 2734 t.Errorf("wrong result\ngot: %swant: %s", dump.Value(got), dump.Value(test.Want)) 2735 } 2736 }) 2737 } 2738 } 2739 2740 var testAttributes = map[string]*configschema.Attribute{ 2741 "optional": { 2742 Type: cty.String, 2743 Optional: true, 2744 }, 2745 "computed": { 2746 Type: cty.String, 2747 Computed: true, 2748 }, 2749 "optional_computed": { 2750 Type: cty.String, 2751 Computed: true, 2752 Optional: true, 2753 }, 2754 "required": { 2755 Type: cty.String, 2756 Required: true, 2757 }, 2758 }