github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/context_validate_test.go (about) 1 package terraform 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 "testing" 8 9 "github.com/zclconf/go-cty/cty" 10 11 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 12 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 13 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 14 "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" 15 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 16 "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" 17 ) 18 19 func TestContext2Validate_badCount(t *testing.T) { 20 p := testProvider("aws") 21 p.GetSchemaReturn = &ProviderSchema{ 22 ResourceTypes: map[string]*configschema.Block{ 23 "aws_instance": { 24 Attributes: map[string]*configschema.Attribute{}, 25 }, 26 }, 27 } 28 29 m := testModule(t, "validate-bad-count") 30 c := testContext2(t, &ContextOpts{ 31 Config: m, 32 ProviderResolver: providers.ResolverFixed( 33 map[string]providers.Factory{ 34 "aws": testProviderFuncFixed(p), 35 }, 36 ), 37 }) 38 39 diags := c.Validate() 40 if !diags.HasErrors() { 41 t.Fatalf("succeeded; want error") 42 } 43 } 44 45 func TestContext2Validate_badVar(t *testing.T) { 46 p := testProvider("aws") 47 p.GetSchemaReturn = &ProviderSchema{ 48 ResourceTypes: map[string]*configschema.Block{ 49 "aws_instance": { 50 Attributes: map[string]*configschema.Attribute{ 51 "foo": {Type: cty.String, Optional: true}, 52 "num": {Type: cty.String, Optional: true}, 53 }, 54 }, 55 }, 56 } 57 58 m := testModule(t, "validate-bad-var") 59 c := testContext2(t, &ContextOpts{ 60 Config: m, 61 ProviderResolver: providers.ResolverFixed( 62 map[string]providers.Factory{ 63 "aws": testProviderFuncFixed(p), 64 }, 65 ), 66 }) 67 68 diags := c.Validate() 69 if !diags.HasErrors() { 70 t.Fatalf("succeeded; want error") 71 } 72 } 73 74 func TestContext2Validate_varMapOverrideOld(t *testing.T) { 75 m := testModule(t, "validate-module-pc-vars") 76 p := testProvider("aws") 77 p.GetSchemaReturn = &ProviderSchema{ 78 Provider: &configschema.Block{ 79 Attributes: map[string]*configschema.Attribute{ 80 "foo": {Type: cty.String, Optional: true}, 81 }, 82 }, 83 ResourceTypes: map[string]*configschema.Block{ 84 "aws_instance": { 85 Attributes: map[string]*configschema.Attribute{}, 86 }, 87 }, 88 } 89 90 c := testContext2(t, &ContextOpts{ 91 Config: m, 92 ProviderResolver: providers.ResolverFixed( 93 map[string]providers.Factory{ 94 "aws": testProviderFuncFixed(p), 95 }, 96 ), 97 Variables: InputValues{ 98 "foo.foo": &InputValue{ 99 Value: cty.StringVal("bar"), 100 SourceType: ValueFromCaller, 101 }, 102 }, 103 }) 104 105 diags := c.Validate() 106 if !diags.HasErrors() { 107 t.Fatalf("succeeded; want error") 108 } 109 } 110 111 func TestContext2Validate_varNoDefaultExplicitType(t *testing.T) { 112 m := testModule(t, "validate-var-no-default-explicit-type") 113 c := testContext2(t, &ContextOpts{ 114 Config: m, 115 }) 116 117 diags := c.Validate() 118 if !diags.HasErrors() { 119 t.Fatalf("succeeded; want error") 120 } 121 } 122 123 func TestContext2Validate_computedVar(t *testing.T) { 124 p := testProvider("aws") 125 p.GetSchemaReturn = &ProviderSchema{ 126 Provider: &configschema.Block{ 127 Attributes: map[string]*configschema.Attribute{ 128 "value": {Type: cty.String, Optional: true}, 129 }, 130 }, 131 ResourceTypes: map[string]*configschema.Block{ 132 "aws_instance": { 133 Attributes: map[string]*configschema.Attribute{}, 134 }, 135 }, 136 } 137 pt := testProvider("test") 138 pt.GetSchemaReturn = &ProviderSchema{ 139 ResourceTypes: map[string]*configschema.Block{ 140 "test_instance": { 141 Attributes: map[string]*configschema.Attribute{ 142 "value": {Type: cty.String, Optional: true}, 143 }, 144 }, 145 }, 146 } 147 148 m := testModule(t, "validate-computed-var") 149 c := testContext2(t, &ContextOpts{ 150 Config: m, 151 ProviderResolver: providers.ResolverFixed( 152 map[string]providers.Factory{ 153 "aws": testProviderFuncFixed(p), 154 "test": testProviderFuncFixed(pt), 155 }, 156 ), 157 }) 158 159 p.ValidateFn = func(c *ResourceConfig) ([]string, []error) { 160 if !c.IsComputed("value") { 161 return nil, []error{fmt.Errorf("value isn't computed")} 162 } 163 164 return nil, c.CheckSet([]string{"value"}) 165 } 166 167 p.ConfigureFn = func(c *ResourceConfig) error { 168 return fmt.Errorf("Configure should not be called for provider") 169 } 170 171 diags := c.Validate() 172 if diags.HasErrors() { 173 t.Fatalf("unexpected error: %s", diags.Err()) 174 } 175 } 176 177 func TestContext2Validate_computedInFunction(t *testing.T) { 178 p := testProvider("aws") 179 p.GetSchemaReturn = &ProviderSchema{ 180 ResourceTypes: map[string]*configschema.Block{ 181 "aws_instance": { 182 Attributes: map[string]*configschema.Attribute{ 183 "attr": {Type: cty.Number, Optional: true}, 184 }, 185 }, 186 }, 187 DataSources: map[string]*configschema.Block{ 188 "aws_data_source": { 189 Attributes: map[string]*configschema.Attribute{ 190 "optional_attr": {Type: cty.String, Optional: true}, 191 "computed": {Type: cty.String, Computed: true}, 192 }, 193 }, 194 }, 195 } 196 197 m := testModule(t, "validate-computed-in-function") 198 c := testContext2(t, &ContextOpts{ 199 Config: m, 200 ProviderResolver: providers.ResolverFixed( 201 map[string]providers.Factory{ 202 "aws": testProviderFuncFixed(p), 203 }, 204 ), 205 }) 206 207 diags := c.Validate() 208 if diags.HasErrors() { 209 t.Fatalf("unexpected error: %s", diags.Err()) 210 } 211 } 212 213 // Test that validate allows through computed counts. We do this and allow 214 // them to fail during "plan" since we can't know if the computed values 215 // can be realized during a plan. 216 func TestContext2Validate_countComputed(t *testing.T) { 217 p := testProvider("aws") 218 p.GetSchemaReturn = &ProviderSchema{ 219 ResourceTypes: map[string]*configschema.Block{ 220 "aws_instance": { 221 Attributes: map[string]*configschema.Attribute{}, 222 }, 223 }, 224 DataSources: map[string]*configschema.Block{ 225 "aws_data_source": { 226 Attributes: map[string]*configschema.Attribute{ 227 "compute": {Type: cty.String, Optional: true}, 228 "value": {Type: cty.String, Computed: true}, 229 }, 230 }, 231 }, 232 } 233 234 m := testModule(t, "validate-count-computed") 235 c := testContext2(t, &ContextOpts{ 236 Config: m, 237 ProviderResolver: providers.ResolverFixed( 238 map[string]providers.Factory{ 239 "aws": testProviderFuncFixed(p), 240 }, 241 ), 242 }) 243 244 diags := c.Validate() 245 if diags.HasErrors() { 246 t.Fatalf("unexpected error: %s", diags.Err()) 247 } 248 } 249 250 func TestContext2Validate_countNegative(t *testing.T) { 251 p := testProvider("aws") 252 p.GetSchemaReturn = &ProviderSchema{ 253 ResourceTypes: map[string]*configschema.Block{ 254 "aws_instance": { 255 Attributes: map[string]*configschema.Attribute{}, 256 }, 257 }, 258 } 259 260 m := testModule(t, "validate-count-negative") 261 c := testContext2(t, &ContextOpts{ 262 Config: m, 263 ProviderResolver: providers.ResolverFixed( 264 map[string]providers.Factory{ 265 "aws": testProviderFuncFixed(p), 266 }, 267 ), 268 }) 269 270 diags := c.Validate() 271 if !diags.HasErrors() { 272 t.Fatalf("succeeded; want error") 273 } 274 } 275 276 func TestContext2Validate_countVariable(t *testing.T) { 277 p := testProvider("aws") 278 p.GetSchemaReturn = &ProviderSchema{ 279 ResourceTypes: map[string]*configschema.Block{ 280 "aws_instance": { 281 Attributes: map[string]*configschema.Attribute{ 282 "foo": {Type: cty.String, Optional: true}, 283 }, 284 }, 285 }, 286 } 287 288 m := testModule(t, "apply-count-variable") 289 c := testContext2(t, &ContextOpts{ 290 Config: m, 291 ProviderResolver: providers.ResolverFixed( 292 map[string]providers.Factory{ 293 "aws": testProviderFuncFixed(p), 294 }, 295 ), 296 }) 297 298 diags := c.Validate() 299 if diags.HasErrors() { 300 t.Fatalf("unexpected error: %s", diags.Err()) 301 } 302 } 303 304 func TestContext2Validate_countVariableNoDefault(t *testing.T) { 305 p := testProvider("aws") 306 m := testModule(t, "validate-count-variable") 307 p.GetSchemaReturn = &ProviderSchema{ 308 ResourceTypes: map[string]*configschema.Block{ 309 "aws_instance": { 310 Attributes: map[string]*configschema.Attribute{ 311 "foo": {Type: cty.String, Optional: true}, 312 }, 313 }, 314 }, 315 } 316 317 c := testContext2(t, &ContextOpts{ 318 Config: m, 319 ProviderResolver: providers.ResolverFixed( 320 map[string]providers.Factory{ 321 "aws": testProviderFuncFixed(p), 322 }, 323 ), 324 }) 325 326 diags := c.Validate() 327 if !diags.HasErrors() { 328 t.Fatalf("succeeded; want error") 329 } 330 } 331 332 func TestContext2Validate_moduleBadOutput(t *testing.T) { 333 p := testProvider("aws") 334 p.GetSchemaReturn = &ProviderSchema{ 335 ResourceTypes: map[string]*configschema.Block{ 336 "aws_instance": { 337 Attributes: map[string]*configschema.Attribute{ 338 "foo": {Type: cty.String, Optional: true}, 339 }, 340 }, 341 }, 342 } 343 344 m := testModule(t, "validate-bad-module-output") 345 c := testContext2(t, &ContextOpts{ 346 Config: m, 347 ProviderResolver: providers.ResolverFixed( 348 map[string]providers.Factory{ 349 "aws": testProviderFuncFixed(p), 350 }, 351 ), 352 }) 353 354 diags := c.Validate() 355 if !diags.HasErrors() { 356 t.Fatalf("succeeded; want error") 357 } 358 } 359 360 func TestContext2Validate_moduleGood(t *testing.T) { 361 p := testProvider("aws") 362 p.GetSchemaReturn = &ProviderSchema{ 363 ResourceTypes: map[string]*configschema.Block{ 364 "aws_instance": { 365 Attributes: map[string]*configschema.Attribute{ 366 "foo": {Type: cty.String, Optional: true}, 367 }, 368 }, 369 }, 370 } 371 372 m := testModule(t, "validate-good-module") 373 c := testContext2(t, &ContextOpts{ 374 Config: m, 375 ProviderResolver: providers.ResolverFixed( 376 map[string]providers.Factory{ 377 "aws": testProviderFuncFixed(p), 378 }, 379 ), 380 }) 381 382 diags := c.Validate() 383 if diags.HasErrors() { 384 t.Fatalf("unexpected error: %s", diags.Err()) 385 } 386 } 387 388 func TestContext2Validate_moduleBadResource(t *testing.T) { 389 m := testModule(t, "validate-module-bad-rc") 390 p := testProvider("aws") 391 p.GetSchemaReturn = &ProviderSchema{ 392 ResourceTypes: map[string]*configschema.Block{ 393 "aws_instance": { 394 Attributes: map[string]*configschema.Attribute{}, 395 }, 396 }, 397 } 398 399 c := testContext2(t, &ContextOpts{ 400 Config: m, 401 ProviderResolver: providers.ResolverFixed( 402 map[string]providers.Factory{ 403 "aws": testProviderFuncFixed(p), 404 }, 405 ), 406 }) 407 408 p.ValidateResourceTypeConfigResponse = providers.ValidateResourceTypeConfigResponse{ 409 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), 410 } 411 412 diags := c.Validate() 413 if !diags.HasErrors() { 414 t.Fatalf("succeeded; want error") 415 } 416 } 417 418 func TestContext2Validate_moduleDepsShouldNotCycle(t *testing.T) { 419 m := testModule(t, "validate-module-deps-cycle") 420 p := testProvider("aws") 421 p.GetSchemaReturn = &ProviderSchema{ 422 ResourceTypes: map[string]*configschema.Block{ 423 "aws_instance": { 424 Attributes: map[string]*configschema.Attribute{ 425 "id": {Type: cty.String, Optional: true}, 426 }, 427 }, 428 }, 429 } 430 431 ctx := testContext2(t, &ContextOpts{ 432 Config: m, 433 ProviderResolver: providers.ResolverFixed( 434 map[string]providers.Factory{ 435 "aws": testProviderFuncFixed(p), 436 }, 437 ), 438 }) 439 440 diags := ctx.Validate() 441 if diags.HasErrors() { 442 t.Fatalf("unexpected error: %s", diags.Err()) 443 } 444 } 445 446 func TestContext2Validate_moduleProviderVar(t *testing.T) { 447 m := testModule(t, "validate-module-pc-vars") 448 p := testProvider("aws") 449 p.GetSchemaReturn = &ProviderSchema{ 450 Provider: &configschema.Block{ 451 Attributes: map[string]*configschema.Attribute{ 452 "foo": {Type: cty.String, Optional: true}, 453 }, 454 }, 455 ResourceTypes: map[string]*configschema.Block{ 456 "aws_instance": { 457 Attributes: map[string]*configschema.Attribute{ 458 "foo": {Type: cty.String, Optional: true}, 459 }, 460 }, 461 }, 462 } 463 464 c := testContext2(t, &ContextOpts{ 465 Config: m, 466 ProviderResolver: providers.ResolverFixed( 467 map[string]providers.Factory{ 468 "aws": testProviderFuncFixed(p), 469 }, 470 ), 471 Variables: InputValues{ 472 "provider_var": &InputValue{ 473 Value: cty.StringVal("bar"), 474 SourceType: ValueFromCaller, 475 }, 476 }, 477 }) 478 479 p.ValidateFn = func(c *ResourceConfig) ([]string, []error) { 480 return nil, c.CheckSet([]string{"foo"}) 481 } 482 483 diags := c.Validate() 484 if diags.HasErrors() { 485 t.Fatalf("unexpected error: %s", diags.Err()) 486 } 487 } 488 489 func TestContext2Validate_moduleProviderInheritUnused(t *testing.T) { 490 m := testModule(t, "validate-module-pc-inherit-unused") 491 p := testProvider("aws") 492 p.GetSchemaReturn = &ProviderSchema{ 493 Provider: &configschema.Block{ 494 Attributes: map[string]*configschema.Attribute{ 495 "foo": {Type: cty.String, Optional: true}, 496 }, 497 }, 498 ResourceTypes: map[string]*configschema.Block{ 499 "aws_instance": { 500 Attributes: map[string]*configschema.Attribute{ 501 "foo": {Type: cty.String, Optional: true}, 502 }, 503 }, 504 }, 505 } 506 507 c := testContext2(t, &ContextOpts{ 508 Config: m, 509 ProviderResolver: providers.ResolverFixed( 510 map[string]providers.Factory{ 511 "aws": testProviderFuncFixed(p), 512 }, 513 ), 514 }) 515 516 p.ValidateFn = func(c *ResourceConfig) ([]string, []error) { 517 return nil, c.CheckSet([]string{"foo"}) 518 } 519 520 diags := c.Validate() 521 if diags.HasErrors() { 522 t.Fatalf("unexpected error: %s", diags.Err()) 523 } 524 } 525 526 func TestContext2Validate_orphans(t *testing.T) { 527 p := testProvider("aws") 528 p.GetSchemaReturn = &ProviderSchema{ 529 ResourceTypes: map[string]*configschema.Block{ 530 "aws_instance": { 531 Attributes: map[string]*configschema.Attribute{ 532 "foo": {Type: cty.String, Optional: true}, 533 "num": {Type: cty.String, Optional: true}, 534 }, 535 }, 536 }, 537 } 538 539 m := testModule(t, "validate-good") 540 state := MustShimLegacyState(&State{ 541 Modules: []*ModuleState{ 542 { 543 Path: rootModulePath, 544 Resources: map[string]*ResourceState{ 545 "aws_instance.web": { 546 Type: "aws_instance", 547 Primary: &InstanceState{ 548 ID: "bar", 549 }, 550 }, 551 }, 552 }, 553 }, 554 }) 555 c := testContext2(t, &ContextOpts{ 556 Config: m, 557 ProviderResolver: providers.ResolverFixed( 558 map[string]providers.Factory{ 559 "aws": testProviderFuncFixed(p), 560 }, 561 ), 562 State: state, 563 }) 564 565 p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { 566 var diags tfdiags.Diagnostics 567 if req.Config.GetAttr("foo").IsNull() { 568 diags.Append(errors.New("foo is not set")) 569 } 570 return providers.ValidateResourceTypeConfigResponse{ 571 Diagnostics: diags, 572 } 573 } 574 575 diags := c.Validate() 576 if diags.HasErrors() { 577 t.Fatalf("unexpected error: %s", diags.Err()) 578 } 579 } 580 581 func TestContext2Validate_providerConfig_bad(t *testing.T) { 582 m := testModule(t, "validate-bad-pc") 583 p := testProvider("aws") 584 p.GetSchemaReturn = &ProviderSchema{ 585 Provider: &configschema.Block{ 586 Attributes: map[string]*configschema.Attribute{ 587 "foo": {Type: cty.String, Optional: true}, 588 }, 589 }, 590 ResourceTypes: map[string]*configschema.Block{ 591 "aws_instance": { 592 Attributes: map[string]*configschema.Attribute{}, 593 }, 594 }, 595 } 596 597 c := testContext2(t, &ContextOpts{ 598 Config: m, 599 ProviderResolver: providers.ResolverFixed( 600 map[string]providers.Factory{ 601 "aws": testProviderFuncFixed(p), 602 }, 603 ), 604 }) 605 606 p.PrepareProviderConfigResponse = providers.PrepareProviderConfigResponse{ 607 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), 608 } 609 610 diags := c.Validate() 611 if len(diags) != 1 { 612 t.Fatalf("wrong number of diagnostics %d; want %d", len(diags), 1) 613 } 614 if !strings.Contains(diags.Err().Error(), "bad") { 615 t.Fatalf("bad: %s", diags.Err().Error()) 616 } 617 } 618 619 func TestContext2Validate_providerConfig_badEmpty(t *testing.T) { 620 m := testModule(t, "validate-bad-pc-empty") 621 p := testProvider("aws") 622 p.GetSchemaReturn = &ProviderSchema{ 623 Provider: &configschema.Block{ 624 Attributes: map[string]*configschema.Attribute{ 625 "foo": {Type: cty.String, Optional: true}, 626 }, 627 }, 628 ResourceTypes: map[string]*configschema.Block{ 629 "aws_instance": { 630 Attributes: map[string]*configschema.Attribute{}, 631 }, 632 }, 633 } 634 635 c := testContext2(t, &ContextOpts{ 636 Config: m, 637 ProviderResolver: providers.ResolverFixed( 638 map[string]providers.Factory{ 639 "aws": testProviderFuncFixed(p), 640 }, 641 ), 642 }) 643 644 p.PrepareProviderConfigResponse = providers.PrepareProviderConfigResponse{ 645 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), 646 } 647 648 diags := c.Validate() 649 if !diags.HasErrors() { 650 t.Fatalf("succeeded; want error") 651 } 652 } 653 654 func TestContext2Validate_providerConfig_good(t *testing.T) { 655 m := testModule(t, "validate-bad-pc") 656 p := testProvider("aws") 657 p.GetSchemaReturn = &ProviderSchema{ 658 Provider: &configschema.Block{ 659 Attributes: map[string]*configschema.Attribute{ 660 "foo": {Type: cty.String, Optional: true}, 661 }, 662 }, 663 ResourceTypes: map[string]*configschema.Block{ 664 "aws_instance": { 665 Attributes: map[string]*configschema.Attribute{}, 666 }, 667 }, 668 } 669 670 c := testContext2(t, &ContextOpts{ 671 Config: m, 672 ProviderResolver: providers.ResolverFixed( 673 map[string]providers.Factory{ 674 "aws": testProviderFuncFixed(p), 675 }, 676 ), 677 }) 678 679 diags := c.Validate() 680 if diags.HasErrors() { 681 t.Fatalf("unexpected error: %s", diags.Err()) 682 } 683 } 684 685 func TestContext2Validate_provisionerConfig_bad(t *testing.T) { 686 m := testModule(t, "validate-bad-prov-conf") 687 p := testProvider("aws") 688 p.GetSchemaReturn = &ProviderSchema{ 689 ResourceTypes: map[string]*configschema.Block{ 690 "aws_instance": { 691 Attributes: map[string]*configschema.Attribute{ 692 "foo": {Type: cty.String, Optional: true}, 693 }, 694 }, 695 }, 696 } 697 698 pr := simpleMockProvisioner() 699 700 c := testContext2(t, &ContextOpts{ 701 Config: m, 702 ProviderResolver: providers.ResolverFixed( 703 map[string]providers.Factory{ 704 "aws": testProviderFuncFixed(p), 705 }, 706 ), 707 Provisioners: map[string]ProvisionerFactory{ 708 "shell": testProvisionerFuncFixed(pr), 709 }, 710 }) 711 712 p.PrepareProviderConfigResponse = providers.PrepareProviderConfigResponse{ 713 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), 714 } 715 716 diags := c.Validate() 717 if !diags.HasErrors() { 718 t.Fatalf("succeeded; want error") 719 } 720 } 721 722 func TestContext2Validate_badResourceConnection(t *testing.T) { 723 m := testModule(t, "validate-bad-resource-connection") 724 p := testProvider("aws") 725 p.GetSchemaReturn = &ProviderSchema{ 726 ResourceTypes: map[string]*configschema.Block{ 727 "aws_instance": { 728 Attributes: map[string]*configschema.Attribute{ 729 "foo": {Type: cty.String, Optional: true}, 730 }, 731 }, 732 }, 733 } 734 735 pr := simpleMockProvisioner() 736 737 c := testContext2(t, &ContextOpts{ 738 Config: m, 739 ProviderResolver: providers.ResolverFixed( 740 map[string]providers.Factory{ 741 "aws": testProviderFuncFixed(p), 742 }, 743 ), 744 Provisioners: map[string]ProvisionerFactory{ 745 "shell": testProvisionerFuncFixed(pr), 746 }, 747 }) 748 749 diags := c.Validate() 750 t.Log(diags.Err()) 751 if !diags.HasErrors() { 752 t.Fatalf("succeeded; want error") 753 } 754 } 755 756 func TestContext2Validate_badProvisionerConnection(t *testing.T) { 757 m := testModule(t, "validate-bad-prov-connection") 758 p := testProvider("aws") 759 p.GetSchemaReturn = &ProviderSchema{ 760 ResourceTypes: map[string]*configschema.Block{ 761 "aws_instance": { 762 Attributes: map[string]*configschema.Attribute{ 763 "foo": {Type: cty.String, Optional: true}, 764 }, 765 }, 766 }, 767 } 768 769 pr := simpleMockProvisioner() 770 771 c := testContext2(t, &ContextOpts{ 772 Config: m, 773 ProviderResolver: providers.ResolverFixed( 774 map[string]providers.Factory{ 775 "aws": testProviderFuncFixed(p), 776 }, 777 ), 778 Provisioners: map[string]ProvisionerFactory{ 779 "shell": testProvisionerFuncFixed(pr), 780 }, 781 }) 782 783 diags := c.Validate() 784 t.Log(diags.Err()) 785 if !diags.HasErrors() { 786 t.Fatalf("succeeded; want error") 787 } 788 } 789 790 func TestContext2Validate_provisionerConfig_good(t *testing.T) { 791 m := testModule(t, "validate-bad-prov-conf") 792 p := testProvider("aws") 793 p.GetSchemaReturn = &ProviderSchema{ 794 Provider: &configschema.Block{ 795 Attributes: map[string]*configschema.Attribute{ 796 "foo": {Type: cty.String, Optional: true}, 797 }, 798 }, 799 ResourceTypes: map[string]*configschema.Block{ 800 "aws_instance": { 801 Attributes: map[string]*configschema.Attribute{ 802 "foo": {Type: cty.String, Optional: true}, 803 }, 804 }, 805 }, 806 } 807 808 pr := simpleMockProvisioner() 809 pr.ValidateProvisionerConfigFn = func(req provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse { 810 var diags tfdiags.Diagnostics 811 if req.Config.GetAttr("test_string").IsNull() { 812 diags.Append(errors.New("test_string is not set")) 813 } 814 return provisioners.ValidateProvisionerConfigResponse{ 815 Diagnostics: diags, 816 } 817 } 818 819 c := testContext2(t, &ContextOpts{ 820 Config: m, 821 ProviderResolver: providers.ResolverFixed( 822 map[string]providers.Factory{ 823 "aws": testProviderFuncFixed(p), 824 }, 825 ), 826 Provisioners: map[string]ProvisionerFactory{ 827 "shell": testProvisionerFuncFixed(pr), 828 }, 829 }) 830 831 diags := c.Validate() 832 if diags.HasErrors() { 833 t.Fatalf("unexpected error: %s", diags.Err()) 834 } 835 } 836 837 func TestContext2Validate_requiredVar(t *testing.T) { 838 m := testModule(t, "validate-required-var") 839 p := testProvider("aws") 840 p.GetSchemaReturn = &ProviderSchema{ 841 ResourceTypes: map[string]*configschema.Block{ 842 "aws_instance": { 843 Attributes: map[string]*configschema.Attribute{ 844 "ami": {Type: cty.String, Optional: true}, 845 }, 846 }, 847 }, 848 } 849 850 c := testContext2(t, &ContextOpts{ 851 Config: m, 852 ProviderResolver: providers.ResolverFixed( 853 map[string]providers.Factory{ 854 "aws": testProviderFuncFixed(p), 855 }, 856 ), 857 }) 858 859 diags := c.Validate() 860 if !diags.HasErrors() { 861 t.Fatalf("succeeded; want error") 862 } 863 } 864 865 func TestContext2Validate_resourceConfig_bad(t *testing.T) { 866 m := testModule(t, "validate-bad-rc") 867 p := testProvider("aws") 868 p.GetSchemaReturn = &ProviderSchema{ 869 ResourceTypes: map[string]*configschema.Block{ 870 "aws_instance": { 871 Attributes: map[string]*configschema.Attribute{ 872 "foo": {Type: cty.String, Optional: true}, 873 }, 874 }, 875 }, 876 } 877 878 c := testContext2(t, &ContextOpts{ 879 Config: m, 880 ProviderResolver: providers.ResolverFixed( 881 map[string]providers.Factory{ 882 "aws": testProviderFuncFixed(p), 883 }, 884 ), 885 }) 886 887 p.ValidateResourceTypeConfigResponse = providers.ValidateResourceTypeConfigResponse{ 888 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), 889 } 890 891 diags := c.Validate() 892 if !diags.HasErrors() { 893 t.Fatalf("succeeded; want error") 894 } 895 } 896 897 func TestContext2Validate_resourceConfig_good(t *testing.T) { 898 m := testModule(t, "validate-bad-rc") 899 p := testProvider("aws") 900 p.GetSchemaReturn = &ProviderSchema{ 901 ResourceTypes: map[string]*configschema.Block{ 902 "aws_instance": { 903 Attributes: map[string]*configschema.Attribute{ 904 "foo": {Type: cty.String, Optional: true}, 905 }, 906 }, 907 }, 908 } 909 910 c := testContext2(t, &ContextOpts{ 911 Config: m, 912 ProviderResolver: providers.ResolverFixed( 913 map[string]providers.Factory{ 914 "aws": testProviderFuncFixed(p), 915 }, 916 ), 917 }) 918 919 diags := c.Validate() 920 if diags.HasErrors() { 921 t.Fatalf("unexpected error: %s", diags.Err()) 922 } 923 } 924 925 func TestContext2Validate_tainted(t *testing.T) { 926 p := testProvider("aws") 927 p.GetSchemaReturn = &ProviderSchema{ 928 ResourceTypes: map[string]*configschema.Block{ 929 "aws_instance": { 930 Attributes: map[string]*configschema.Attribute{ 931 "foo": {Type: cty.String, Optional: true}, 932 "num": {Type: cty.String, Optional: true}, 933 }, 934 }, 935 }, 936 } 937 938 m := testModule(t, "validate-good") 939 state := MustShimLegacyState(&State{ 940 Modules: []*ModuleState{ 941 { 942 Path: rootModulePath, 943 Resources: map[string]*ResourceState{ 944 "aws_instance.foo": { 945 Type: "aws_instance", 946 Primary: &InstanceState{ 947 ID: "bar", 948 Tainted: true, 949 }, 950 }, 951 }, 952 }, 953 }, 954 }) 955 c := testContext2(t, &ContextOpts{ 956 Config: m, 957 ProviderResolver: providers.ResolverFixed( 958 map[string]providers.Factory{ 959 "aws": testProviderFuncFixed(p), 960 }, 961 ), 962 State: state, 963 }) 964 965 p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { 966 var diags tfdiags.Diagnostics 967 if req.Config.GetAttr("foo").IsNull() { 968 diags.Append(errors.New("foo is not set")) 969 } 970 return providers.ValidateResourceTypeConfigResponse{ 971 Diagnostics: diags, 972 } 973 } 974 975 diags := c.Validate() 976 if diags.HasErrors() { 977 t.Fatalf("unexpected error: %s", diags.Err()) 978 } 979 } 980 981 func TestContext2Validate_targetedDestroy(t *testing.T) { 982 m := testModule(t, "validate-targeted") 983 p := testProvider("aws") 984 pr := simpleMockProvisioner() 985 p.ApplyFn = testApplyFn 986 p.DiffFn = testDiffFn 987 p.GetSchemaReturn = &ProviderSchema{ 988 ResourceTypes: map[string]*configschema.Block{ 989 "aws_instance": { 990 Attributes: map[string]*configschema.Attribute{ 991 "foo": {Type: cty.String, Optional: true}, 992 "num": {Type: cty.String, Optional: true}, 993 }, 994 }, 995 }, 996 } 997 998 ctx := testContext2(t, &ContextOpts{ 999 Config: m, 1000 ProviderResolver: providers.ResolverFixed( 1001 map[string]providers.Factory{ 1002 "aws": testProviderFuncFixed(p), 1003 }, 1004 ), 1005 Provisioners: map[string]ProvisionerFactory{ 1006 "shell": testProvisionerFuncFixed(pr), 1007 }, 1008 State: MustShimLegacyState(&State{ 1009 Modules: []*ModuleState{ 1010 { 1011 Path: rootModulePath, 1012 Resources: map[string]*ResourceState{ 1013 "aws_instance.foo": resourceState("aws_instance", "i-bcd345"), 1014 "aws_instance.bar": resourceState("aws_instance", "i-abc123"), 1015 }, 1016 }, 1017 }, 1018 }), 1019 Targets: []addrs.Targetable{ 1020 addrs.RootModuleInstance.Resource( 1021 addrs.ManagedResourceMode, "aws_instance", "foo", 1022 ), 1023 }, 1024 Destroy: true, 1025 }) 1026 1027 diags := ctx.Validate() 1028 if diags.HasErrors() { 1029 t.Fatalf("unexpected error: %s", diags.Err()) 1030 } 1031 } 1032 1033 func TestContext2Validate_varRefUnknown(t *testing.T) { 1034 m := testModule(t, "validate-variable-ref") 1035 p := testProvider("aws") 1036 p.GetSchemaReturn = &ProviderSchema{ 1037 ResourceTypes: map[string]*configschema.Block{ 1038 "aws_instance": { 1039 Attributes: map[string]*configschema.Attribute{ 1040 "foo": {Type: cty.String, Optional: true}, 1041 }, 1042 }, 1043 }, 1044 } 1045 c := testContext2(t, &ContextOpts{ 1046 Config: m, 1047 ProviderResolver: providers.ResolverFixed( 1048 map[string]providers.Factory{ 1049 "aws": testProviderFuncFixed(p), 1050 }, 1051 ), 1052 Variables: InputValues{ 1053 "foo": &InputValue{ 1054 Value: cty.StringVal("bar"), 1055 SourceType: ValueFromCaller, 1056 }, 1057 }, 1058 }) 1059 1060 var value cty.Value 1061 p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { 1062 value = req.Config.GetAttr("foo") 1063 return providers.ValidateResourceTypeConfigResponse{} 1064 } 1065 1066 c.Validate() 1067 1068 // Input variables are always unknown during the validate walk, because 1069 // we're checking for validity of all possible input values. Validity 1070 // against specific input values is checked during the plan walk. 1071 if !value.RawEquals(cty.UnknownVal(cty.String)) { 1072 t.Fatalf("bad: %#v", value) 1073 } 1074 } 1075 1076 // Module variables weren't being interpolated during Validate phase. 1077 // related to https://github.com/hashicorp/terraform-plugin-sdk/issues/5322 1078 func TestContext2Validate_interpolateVar(t *testing.T) { 1079 input := new(MockUIInput) 1080 1081 m := testModule(t, "input-interpolate-var") 1082 p := testProvider("null") 1083 p.ApplyFn = testApplyFn 1084 p.DiffFn = testDiffFn 1085 p.GetSchemaReturn = &ProviderSchema{ 1086 ResourceTypes: map[string]*configschema.Block{ 1087 "template_file": { 1088 Attributes: map[string]*configschema.Attribute{ 1089 "template": {Type: cty.String, Optional: true}, 1090 }, 1091 }, 1092 }, 1093 } 1094 1095 ctx := testContext2(t, &ContextOpts{ 1096 Config: m, 1097 ProviderResolver: providers.ResolverFixed( 1098 map[string]providers.Factory{ 1099 "template": testProviderFuncFixed(p), 1100 }, 1101 ), 1102 UIInput: input, 1103 }) 1104 1105 diags := ctx.Validate() 1106 if diags.HasErrors() { 1107 t.Fatalf("unexpected error: %s", diags.Err()) 1108 } 1109 } 1110 1111 // When module vars reference something that is actually computed, this 1112 // shouldn't cause validation to fail. 1113 func TestContext2Validate_interpolateComputedModuleVarDef(t *testing.T) { 1114 input := new(MockUIInput) 1115 1116 m := testModule(t, "validate-computed-module-var-ref") 1117 p := testProvider("aws") 1118 p.ApplyFn = testApplyFn 1119 p.DiffFn = testDiffFn 1120 p.GetSchemaReturn = &ProviderSchema{ 1121 ResourceTypes: map[string]*configschema.Block{ 1122 "aws_instance": { 1123 Attributes: map[string]*configschema.Attribute{ 1124 "attr": {Type: cty.String, Optional: true}, 1125 }, 1126 }, 1127 }, 1128 } 1129 1130 ctx := testContext2(t, &ContextOpts{ 1131 Config: m, 1132 ProviderResolver: providers.ResolverFixed( 1133 map[string]providers.Factory{ 1134 "aws": testProviderFuncFixed(p), 1135 }, 1136 ), 1137 UIInput: input, 1138 }) 1139 1140 diags := ctx.Validate() 1141 if diags.HasErrors() { 1142 t.Fatalf("unexpected error: %s", diags.Err()) 1143 } 1144 } 1145 1146 // Computed values are lost when a map is output from a module 1147 func TestContext2Validate_interpolateMap(t *testing.T) { 1148 input := new(MockUIInput) 1149 1150 m := testModule(t, "issue-9549") 1151 p := testProvider("template") 1152 p.ApplyFn = testApplyFn 1153 p.DiffFn = testDiffFn 1154 1155 ctx := testContext2(t, &ContextOpts{ 1156 Config: m, 1157 ProviderResolver: providers.ResolverFixed( 1158 map[string]providers.Factory{ 1159 "template": testProviderFuncFixed(p), 1160 }, 1161 ), 1162 UIInput: input, 1163 }) 1164 1165 diags := ctx.Validate() 1166 if diags.HasErrors() { 1167 t.Fatalf("unexpected error: %s", diags.Err()) 1168 } 1169 } 1170 1171 // Manually validate using the new PlanGraphBuilder 1172 func TestContext2Validate_PlanGraphBuilder(t *testing.T) { 1173 fixture := contextFixtureApplyVars(t) 1174 opts := fixture.ContextOpts() 1175 opts.Variables = InputValues{ 1176 "foo": &InputValue{ 1177 Value: cty.StringVal("us-east-1"), 1178 SourceType: ValueFromCaller, 1179 }, 1180 "test_list": &InputValue{ 1181 Value: cty.ListVal([]cty.Value{ 1182 cty.StringVal("Hello"), 1183 cty.StringVal("World"), 1184 }), 1185 SourceType: ValueFromCaller, 1186 }, 1187 "test_map": &InputValue{ 1188 Value: cty.MapVal(map[string]cty.Value{ 1189 "Hello": cty.StringVal("World"), 1190 "Foo": cty.StringVal("Bar"), 1191 "Baz": cty.StringVal("Foo"), 1192 }), 1193 SourceType: ValueFromCaller, 1194 }, 1195 "amis": &InputValue{ 1196 Value: cty.MapVal(map[string]cty.Value{ 1197 "us-east-1": cty.StringVal("override"), 1198 }), 1199 SourceType: ValueFromCaller, 1200 }, 1201 } 1202 c := testContext2(t, opts) 1203 1204 graph, diags := (&PlanGraphBuilder{ 1205 Config: c.config, 1206 State: states.NewState(), 1207 Components: c.components, 1208 Schemas: c.schemas, 1209 Targets: c.targets, 1210 }).Build(addrs.RootModuleInstance) 1211 if diags.HasErrors() { 1212 t.Fatalf("errors from PlanGraphBuilder: %s", diags.Err()) 1213 } 1214 defer c.acquireRun("validate-test")() 1215 walker, diags := c.walk(graph, walkValidate) 1216 if diags.HasErrors() { 1217 t.Fatal(diags.Err()) 1218 } 1219 if len(walker.NonFatalDiagnostics) > 0 { 1220 t.Fatal(walker.NonFatalDiagnostics.Err()) 1221 } 1222 } 1223 1224 func TestContext2Validate_invalidOutput(t *testing.T) { 1225 m := testModuleInline(t, map[string]string{ 1226 "main.tf": ` 1227 data "aws_data_source" "name" {} 1228 1229 output "out" { 1230 value = "${data.aws_data_source.name.missing}" 1231 }`, 1232 }) 1233 1234 p := testProvider("aws") 1235 ctx := testContext2(t, &ContextOpts{ 1236 Config: m, 1237 ProviderResolver: providers.ResolverFixed( 1238 map[string]providers.Factory{ 1239 "aws": testProviderFuncFixed(p), 1240 }, 1241 ), 1242 }) 1243 1244 diags := ctx.Validate() 1245 if !diags.HasErrors() { 1246 t.Fatal("succeeded; want errors") 1247 } 1248 // Should get this error: 1249 // Unsupported attribute: This object does not have an attribute named "missing" 1250 if got, want := diags.Err().Error(), "Unsupported attribute"; strings.Index(got, want) == -1 { 1251 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1252 } 1253 } 1254 1255 func TestContext2Validate_invalidModuleOutput(t *testing.T) { 1256 m := testModuleInline(t, map[string]string{ 1257 "child/main.tf": ` 1258 data "aws_data_source" "name" {} 1259 1260 output "out" { 1261 value = "${data.aws_data_source.name.missing}" 1262 }`, 1263 "main.tf": ` 1264 module "child" { 1265 source = "./child" 1266 } 1267 1268 resource "aws_instance" "foo" { 1269 foo = "${module.child.out}" 1270 }`, 1271 }) 1272 1273 p := testProvider("aws") 1274 ctx := testContext2(t, &ContextOpts{ 1275 Config: m, 1276 ProviderResolver: providers.ResolverFixed( 1277 map[string]providers.Factory{ 1278 "aws": testProviderFuncFixed(p), 1279 }, 1280 ), 1281 }) 1282 1283 diags := ctx.Validate() 1284 if !diags.HasErrors() { 1285 t.Fatal("succeeded; want errors") 1286 } 1287 // Should get this error: 1288 // Unsupported attribute: This object does not have an attribute named "missing" 1289 if got, want := diags.Err().Error(), "Unsupported attribute"; strings.Index(got, want) == -1 { 1290 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1291 } 1292 } 1293 1294 func TestContext2Validate_legacyResourceCount(t *testing.T) { 1295 m := testModuleInline(t, map[string]string{ 1296 "main.tf": ` 1297 resource "aws_instance" "test" {} 1298 1299 output "out" { 1300 value = aws_instance.test.count 1301 }`, 1302 }) 1303 1304 p := testProvider("aws") 1305 ctx := testContext2(t, &ContextOpts{ 1306 Config: m, 1307 ProviderResolver: providers.ResolverFixed( 1308 map[string]providers.Factory{ 1309 "aws": testProviderFuncFixed(p), 1310 }, 1311 ), 1312 }) 1313 1314 diags := ctx.Validate() 1315 if !diags.HasErrors() { 1316 t.Fatal("succeeded; want errors") 1317 } 1318 // Should get this error: 1319 // Invalid resource count attribute: The special "count" attribute is no longer supported after Terraform v0.12. Instead, use length(aws_instance.test) to count resource instances. 1320 if got, want := diags.Err().Error(), "Invalid resource count attribute:"; strings.Index(got, want) == -1 { 1321 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1322 } 1323 } 1324 1325 func TestContext2Validate_invalidModuleRef(t *testing.T) { 1326 // This test is verifying that we properly validate and report on references 1327 // to modules that are not declared, since we were missing some validation 1328 // here in early 0.12.0 alphas that led to a panic. 1329 m := testModuleInline(t, map[string]string{ 1330 "main.tf": ` 1331 output "out" { 1332 # Intentionally referencing undeclared module to ensure error 1333 value = module.foo 1334 }`, 1335 }) 1336 1337 p := testProvider("aws") 1338 ctx := testContext2(t, &ContextOpts{ 1339 Config: m, 1340 ProviderResolver: providers.ResolverFixed( 1341 map[string]providers.Factory{ 1342 "aws": testProviderFuncFixed(p), 1343 }, 1344 ), 1345 }) 1346 1347 diags := ctx.Validate() 1348 if !diags.HasErrors() { 1349 t.Fatal("succeeded; want errors") 1350 } 1351 // Should get this error: 1352 // Reference to undeclared module: No module call named "foo" is declared in the root module. 1353 if got, want := diags.Err().Error(), "Reference to undeclared module:"; strings.Index(got, want) == -1 { 1354 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1355 } 1356 } 1357 1358 func TestContext2Validate_invalidModuleOutputRef(t *testing.T) { 1359 // This test is verifying that we properly validate and report on references 1360 // to modules that are not declared, since we were missing some validation 1361 // here in early 0.12.0 alphas that led to a panic. 1362 m := testModuleInline(t, map[string]string{ 1363 "main.tf": ` 1364 output "out" { 1365 # Intentionally referencing undeclared module to ensure error 1366 value = module.foo.bar 1367 }`, 1368 }) 1369 1370 p := testProvider("aws") 1371 ctx := testContext2(t, &ContextOpts{ 1372 Config: m, 1373 ProviderResolver: providers.ResolverFixed( 1374 map[string]providers.Factory{ 1375 "aws": testProviderFuncFixed(p), 1376 }, 1377 ), 1378 }) 1379 1380 diags := ctx.Validate() 1381 if !diags.HasErrors() { 1382 t.Fatal("succeeded; want errors") 1383 } 1384 // Should get this error: 1385 // Reference to undeclared module: No module call named "foo" is declared in the root module. 1386 if got, want := diags.Err().Error(), "Reference to undeclared module:"; strings.Index(got, want) == -1 { 1387 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1388 } 1389 } 1390 1391 func TestContext2Validate_invalidDependsOnResourceRef(t *testing.T) { 1392 // This test is verifying that we raise an error if depends_on 1393 // refers to something that doesn't exist in configuration. 1394 m := testModuleInline(t, map[string]string{ 1395 "main.tf": ` 1396 resource "test_instance" "bar" { 1397 depends_on = [test_resource.nonexistant] 1398 } 1399 `, 1400 }) 1401 1402 p := testProvider("test") 1403 ctx := testContext2(t, &ContextOpts{ 1404 Config: m, 1405 ProviderResolver: providers.ResolverFixed( 1406 map[string]providers.Factory{ 1407 "test": testProviderFuncFixed(p), 1408 }, 1409 ), 1410 }) 1411 1412 diags := ctx.Validate() 1413 if !diags.HasErrors() { 1414 t.Fatal("succeeded; want errors") 1415 } 1416 // Should get this error: 1417 // Reference to undeclared module: No module call named "foo" is declared in the root module. 1418 if got, want := diags.Err().Error(), "Reference to undeclared resource:"; strings.Index(got, want) == -1 { 1419 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1420 } 1421 } 1422 1423 func TestContext2Validate_invalidResourceIgnoreChanges(t *testing.T) { 1424 // This test is verifying that we raise an error if ignore_changes 1425 // refers to something that can be statically detected as not conforming 1426 // to the resource type schema. 1427 m := testModuleInline(t, map[string]string{ 1428 "main.tf": ` 1429 resource "test_instance" "bar" { 1430 lifecycle { 1431 ignore_changes = [does_not_exist_in_schema] 1432 } 1433 } 1434 `, 1435 }) 1436 1437 p := testProvider("test") 1438 ctx := testContext2(t, &ContextOpts{ 1439 Config: m, 1440 ProviderResolver: providers.ResolverFixed( 1441 map[string]providers.Factory{ 1442 "test": testProviderFuncFixed(p), 1443 }, 1444 ), 1445 }) 1446 1447 diags := ctx.Validate() 1448 if !diags.HasErrors() { 1449 t.Fatal("succeeded; want errors") 1450 } 1451 // Should get this error: 1452 // Reference to undeclared module: No module call named "foo" is declared in the root module. 1453 if got, want := diags.Err().Error(), `no argument, nested block, or exported attribute named "does_not_exist_in_schema"`; strings.Index(got, want) == -1 { 1454 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1455 } 1456 }