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