github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/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/iaas-resource-provision/iaas-rpc/internal/addrs" 12 "github.com/iaas-resource-provision/iaas-rpc/internal/configs/configschema" 13 "github.com/iaas-resource-provision/iaas-rpc/internal/plans" 14 "github.com/iaas-resource-provision/iaas-rpc/internal/providers" 15 "github.com/iaas-resource-provision/iaas-rpc/internal/provisioners" 16 "github.com/iaas-resource-provision/iaas-rpc/internal/states" 17 "github.com/iaas-resource-provision/iaas-rpc/internal/tfdiags" 18 ) 19 20 func TestContext2Validate_badCount(t *testing.T) { 21 p := testProvider("aws") 22 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 23 ResourceTypes: map[string]*configschema.Block{ 24 "aws_instance": { 25 Attributes: map[string]*configschema.Attribute{}, 26 }, 27 }, 28 }) 29 30 m := testModule(t, "validate-bad-count") 31 c := testContext2(t, &ContextOpts{ 32 Config: m, 33 Providers: map[addrs.Provider]providers.Factory{ 34 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 35 }, 36 }) 37 38 diags := c.Validate() 39 if !diags.HasErrors() { 40 t.Fatalf("succeeded; want error") 41 } 42 } 43 44 func TestContext2Validate_badResource_reference(t *testing.T) { 45 p := testProvider("aws") 46 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 47 ResourceTypes: map[string]*configschema.Block{ 48 "aws_instance": { 49 Attributes: map[string]*configschema.Attribute{}, 50 }, 51 }, 52 }) 53 54 m := testModule(t, "validate-bad-resource-count") 55 c := testContext2(t, &ContextOpts{ 56 Config: m, 57 Providers: map[addrs.Provider]providers.Factory{ 58 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 59 }, 60 }) 61 62 diags := c.Validate() 63 if !diags.HasErrors() { 64 t.Fatalf("succeeded; want error") 65 } 66 } 67 68 func TestContext2Validate_badVar(t *testing.T) { 69 p := testProvider("aws") 70 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 71 ResourceTypes: map[string]*configschema.Block{ 72 "aws_instance": { 73 Attributes: map[string]*configschema.Attribute{ 74 "foo": {Type: cty.String, Optional: true}, 75 "num": {Type: cty.String, Optional: true}, 76 }, 77 }, 78 }, 79 }) 80 81 m := testModule(t, "validate-bad-var") 82 c := testContext2(t, &ContextOpts{ 83 Config: m, 84 Providers: map[addrs.Provider]providers.Factory{ 85 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 86 }, 87 }) 88 89 diags := c.Validate() 90 if !diags.HasErrors() { 91 t.Fatalf("succeeded; want error") 92 } 93 } 94 95 func TestContext2Validate_varMapOverrideOld(t *testing.T) { 96 m := testModule(t, "validate-module-pc-vars") 97 p := testProvider("aws") 98 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 99 Provider: &configschema.Block{ 100 Attributes: map[string]*configschema.Attribute{ 101 "foo": {Type: cty.String, Optional: true}, 102 }, 103 }, 104 ResourceTypes: map[string]*configschema.Block{ 105 "aws_instance": { 106 Attributes: map[string]*configschema.Attribute{}, 107 }, 108 }, 109 }) 110 111 _, diags := NewContext(&ContextOpts{ 112 Config: m, 113 Providers: map[addrs.Provider]providers.Factory{ 114 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 115 }, 116 Variables: InputValues{}, 117 }) 118 if !diags.HasErrors() { 119 // Error should be: The input variable "provider_var" has not been assigned a value. 120 t.Fatalf("succeeded; want error") 121 } 122 } 123 124 func TestContext2Validate_varNoDefaultExplicitType(t *testing.T) { 125 m := testModule(t, "validate-var-no-default-explicit-type") 126 _, diags := NewContext(&ContextOpts{ 127 Config: m, 128 }) 129 if !diags.HasErrors() { 130 // Error should be: The input variable "maybe_a_map" has not been assigned a value. 131 t.Fatalf("succeeded; want error") 132 } 133 } 134 135 func TestContext2Validate_computedVar(t *testing.T) { 136 p := testProvider("aws") 137 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 138 Provider: providers.Schema{ 139 Block: &configschema.Block{ 140 Attributes: map[string]*configschema.Attribute{ 141 "value": {Type: cty.String, Optional: true}, 142 }, 143 }, 144 }, 145 ResourceTypes: map[string]providers.Schema{ 146 "aws_instance": { 147 Block: &configschema.Block{ 148 Attributes: map[string]*configschema.Attribute{}, 149 }, 150 }, 151 }, 152 } 153 pt := testProvider("test") 154 pt.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 155 ResourceTypes: map[string]providers.Schema{ 156 "test_instance": { 157 Block: &configschema.Block{ 158 Attributes: map[string]*configschema.Attribute{ 159 "id": {Type: cty.String, Computed: true}, 160 "value": {Type: cty.String, Optional: true}, 161 }, 162 }, 163 }, 164 }, 165 } 166 167 m := testModule(t, "validate-computed-var") 168 c := testContext2(t, &ContextOpts{ 169 Config: m, 170 Providers: map[addrs.Provider]providers.Factory{ 171 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 172 addrs.NewDefaultProvider("test"): testProviderFuncFixed(pt), 173 }, 174 }) 175 176 p.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { 177 val := req.Config.GetAttr("value") 178 if val.IsKnown() { 179 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value isn't computed")) 180 } 181 182 return 183 } 184 185 diags := c.Validate() 186 if diags.HasErrors() { 187 t.Fatalf("unexpected error: %s", diags.Err()) 188 } 189 if p.ConfigureProviderCalled { 190 t.Fatal("Configure should not be called for provider") 191 } 192 } 193 194 func TestContext2Validate_computedInFunction(t *testing.T) { 195 p := testProvider("aws") 196 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 197 ResourceTypes: map[string]providers.Schema{ 198 "aws_instance": { 199 Block: &configschema.Block{ 200 Attributes: map[string]*configschema.Attribute{ 201 "attr": {Type: cty.Number, Optional: true}, 202 }, 203 }, 204 }, 205 }, 206 DataSources: map[string]providers.Schema{ 207 "aws_data_source": { 208 Block: &configschema.Block{ 209 Attributes: map[string]*configschema.Attribute{ 210 "optional_attr": {Type: cty.String, Optional: true}, 211 "computed": {Type: cty.String, Computed: true}, 212 }, 213 }, 214 }, 215 }, 216 } 217 218 m := testModule(t, "validate-computed-in-function") 219 c := testContext2(t, &ContextOpts{ 220 Config: m, 221 Providers: map[addrs.Provider]providers.Factory{ 222 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 223 }, 224 }) 225 226 diags := c.Validate() 227 if diags.HasErrors() { 228 t.Fatalf("unexpected error: %s", diags.Err()) 229 } 230 } 231 232 // Test that validate allows through computed counts. We do this and allow 233 // them to fail during "plan" since we can't know if the computed values 234 // can be realized during a plan. 235 func TestContext2Validate_countComputed(t *testing.T) { 236 p := testProvider("aws") 237 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 238 ResourceTypes: map[string]providers.Schema{ 239 "aws_instance": { 240 Block: &configschema.Block{ 241 Attributes: map[string]*configschema.Attribute{}, 242 }, 243 }, 244 }, 245 DataSources: map[string]providers.Schema{ 246 "aws_data_source": { 247 Block: &configschema.Block{ 248 Attributes: map[string]*configschema.Attribute{ 249 "compute": {Type: cty.String, Optional: true}, 250 "value": {Type: cty.String, Computed: true}, 251 }, 252 }, 253 }, 254 }, 255 } 256 257 m := testModule(t, "validate-count-computed") 258 c := testContext2(t, &ContextOpts{ 259 Config: m, 260 Providers: map[addrs.Provider]providers.Factory{ 261 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 262 }, 263 }) 264 265 diags := c.Validate() 266 if diags.HasErrors() { 267 t.Fatalf("unexpected error: %s", diags.Err()) 268 } 269 } 270 271 func TestContext2Validate_countNegative(t *testing.T) { 272 p := testProvider("aws") 273 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 274 ResourceTypes: map[string]providers.Schema{ 275 "aws_instance": { 276 Block: &configschema.Block{ 277 Attributes: map[string]*configschema.Attribute{}, 278 }, 279 }, 280 }, 281 } 282 m := testModule(t, "validate-count-negative") 283 c := testContext2(t, &ContextOpts{ 284 Config: m, 285 Providers: map[addrs.Provider]providers.Factory{ 286 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 287 }, 288 }) 289 290 diags := c.Validate() 291 if !diags.HasErrors() { 292 t.Fatalf("succeeded; want error") 293 } 294 } 295 296 func TestContext2Validate_countVariable(t *testing.T) { 297 p := testProvider("aws") 298 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 299 ResourceTypes: map[string]providers.Schema{ 300 "aws_instance": { 301 Block: &configschema.Block{ 302 Attributes: map[string]*configschema.Attribute{ 303 "foo": {Type: cty.String, Optional: true}, 304 }, 305 }, 306 }, 307 }, 308 } 309 m := testModule(t, "apply-count-variable") 310 c := testContext2(t, &ContextOpts{ 311 Config: m, 312 Providers: map[addrs.Provider]providers.Factory{ 313 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 314 }, 315 }) 316 317 diags := c.Validate() 318 if diags.HasErrors() { 319 t.Fatalf("unexpected error: %s", diags.Err()) 320 } 321 } 322 323 func TestContext2Validate_countVariableNoDefault(t *testing.T) { 324 p := testProvider("aws") 325 m := testModule(t, "validate-count-variable") 326 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 327 ResourceTypes: map[string]providers.Schema{ 328 "aws_instance": { 329 Block: &configschema.Block{ 330 Attributes: map[string]*configschema.Attribute{ 331 "foo": {Type: cty.String, Optional: true}, 332 }, 333 }, 334 }, 335 }, 336 } 337 _, diags := NewContext(&ContextOpts{ 338 Config: m, 339 Providers: map[addrs.Provider]providers.Factory{ 340 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 341 }, 342 }) 343 if !diags.HasErrors() { 344 // Error should be: The input variable "foo" has not been assigned a value. 345 t.Fatalf("succeeded; want error") 346 } 347 } 348 349 func TestContext2Validate_moduleBadOutput(t *testing.T) { 350 p := testProvider("aws") 351 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 352 ResourceTypes: map[string]providers.Schema{ 353 "aws_instance": { 354 Block: &configschema.Block{ 355 Attributes: map[string]*configschema.Attribute{ 356 "foo": {Type: cty.String, Optional: true}, 357 }, 358 }, 359 }, 360 }, 361 } 362 m := testModule(t, "validate-bad-module-output") 363 c := testContext2(t, &ContextOpts{ 364 Config: m, 365 Providers: map[addrs.Provider]providers.Factory{ 366 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 367 }, 368 }) 369 370 diags := c.Validate() 371 if !diags.HasErrors() { 372 t.Fatalf("succeeded; want error") 373 } 374 } 375 376 func TestContext2Validate_moduleGood(t *testing.T) { 377 p := testProvider("aws") 378 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 379 ResourceTypes: map[string]providers.Schema{ 380 "aws_instance": { 381 Block: &configschema.Block{ 382 Attributes: map[string]*configschema.Attribute{ 383 "foo": {Type: cty.String, Optional: true}, 384 }, 385 }, 386 }, 387 }, 388 } 389 m := testModule(t, "validate-good-module") 390 c := testContext2(t, &ContextOpts{ 391 Config: m, 392 Providers: map[addrs.Provider]providers.Factory{ 393 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 394 }, 395 }) 396 397 diags := c.Validate() 398 if diags.HasErrors() { 399 t.Fatalf("unexpected error: %s", diags.Err()) 400 } 401 } 402 403 func TestContext2Validate_moduleBadResource(t *testing.T) { 404 m := testModule(t, "validate-module-bad-rc") 405 p := testProvider("aws") 406 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 407 ResourceTypes: map[string]providers.Schema{ 408 "aws_instance": { 409 Block: &configschema.Block{ 410 Attributes: map[string]*configschema.Attribute{}, 411 }, 412 }, 413 }, 414 } 415 416 c := testContext2(t, &ContextOpts{ 417 Config: m, 418 Providers: map[addrs.Provider]providers.Factory{ 419 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 420 }, 421 }) 422 423 p.ValidateResourceConfigResponse = &providers.ValidateResourceConfigResponse{ 424 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), 425 } 426 427 diags := c.Validate() 428 if !diags.HasErrors() { 429 t.Fatalf("succeeded; want error") 430 } 431 } 432 433 func TestContext2Validate_moduleDepsShouldNotCycle(t *testing.T) { 434 m := testModule(t, "validate-module-deps-cycle") 435 p := testProvider("aws") 436 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 437 ResourceTypes: map[string]providers.Schema{ 438 "aws_instance": { 439 Block: &configschema.Block{ 440 Attributes: map[string]*configschema.Attribute{ 441 "id": {Type: cty.String, Optional: true}, 442 }, 443 }, 444 }, 445 }, 446 } 447 448 ctx := testContext2(t, &ContextOpts{ 449 Config: m, 450 Providers: map[addrs.Provider]providers.Factory{ 451 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 452 }, 453 }) 454 455 diags := ctx.Validate() 456 if diags.HasErrors() { 457 t.Fatalf("unexpected error: %s", diags.Err()) 458 } 459 } 460 461 func TestContext2Validate_moduleProviderVar(t *testing.T) { 462 m := testModule(t, "validate-module-pc-vars") 463 p := testProvider("aws") 464 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 465 Provider: providers.Schema{ 466 Block: &configschema.Block{ 467 Attributes: map[string]*configschema.Attribute{ 468 "foo": {Type: cty.String, Optional: true}, 469 }, 470 }, 471 }, 472 ResourceTypes: map[string]providers.Schema{ 473 "aws_instance": { 474 Block: &configschema.Block{ 475 Attributes: map[string]*configschema.Attribute{ 476 "foo": {Type: cty.String, Optional: true}, 477 }, 478 }, 479 }, 480 }, 481 } 482 483 c := testContext2(t, &ContextOpts{ 484 Config: m, 485 Providers: map[addrs.Provider]providers.Factory{ 486 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 487 }, 488 Variables: InputValues{ 489 "provider_var": &InputValue{ 490 Value: cty.StringVal("bar"), 491 SourceType: ValueFromCaller, 492 }, 493 }, 494 }) 495 496 p.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { 497 if req.Config.GetAttr("foo").IsNull() { 498 resp.Diagnostics = resp.Diagnostics.Append(errors.New("foo is null")) 499 } 500 return 501 } 502 503 diags := c.Validate() 504 if diags.HasErrors() { 505 t.Fatalf("unexpected error: %s", diags.Err()) 506 } 507 } 508 509 func TestContext2Validate_moduleProviderInheritUnused(t *testing.T) { 510 m := testModule(t, "validate-module-pc-inherit-unused") 511 p := testProvider("aws") 512 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 513 Provider: providers.Schema{ 514 Block: &configschema.Block{ 515 Attributes: map[string]*configschema.Attribute{ 516 "foo": {Type: cty.String, Optional: true}, 517 }, 518 }, 519 }, 520 ResourceTypes: map[string]providers.Schema{ 521 "aws_instance": { 522 Block: &configschema.Block{ 523 Attributes: map[string]*configschema.Attribute{ 524 "foo": {Type: cty.String, Optional: true}, 525 }, 526 }, 527 }, 528 }, 529 } 530 531 c := testContext2(t, &ContextOpts{ 532 Config: m, 533 Providers: map[addrs.Provider]providers.Factory{ 534 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 535 }, 536 }) 537 538 p.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { 539 if req.Config.GetAttr("foo").IsNull() { 540 resp.Diagnostics = resp.Diagnostics.Append(errors.New("foo is null")) 541 } 542 return 543 } 544 545 diags := c.Validate() 546 if diags.HasErrors() { 547 t.Fatalf("unexpected error: %s", diags.Err()) 548 } 549 } 550 551 func TestContext2Validate_orphans(t *testing.T) { 552 p := testProvider("aws") 553 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 554 ResourceTypes: map[string]providers.Schema{ 555 "aws_instance": { 556 Block: &configschema.Block{ 557 Attributes: map[string]*configschema.Attribute{ 558 "foo": {Type: cty.String, Optional: true}, 559 "num": {Type: cty.String, Optional: true}, 560 }, 561 }, 562 }, 563 }, 564 } 565 566 m := testModule(t, "validate-good") 567 568 state := states.NewState() 569 root := state.EnsureModule(addrs.RootModuleInstance) 570 testSetResourceInstanceCurrent(root, "aws_instance.web", `{"id":"bar"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 571 572 c := testContext2(t, &ContextOpts{ 573 Config: m, 574 Providers: map[addrs.Provider]providers.Factory{ 575 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 576 }, 577 State: state, 578 }) 579 580 p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { 581 var diags tfdiags.Diagnostics 582 if req.Config.GetAttr("foo").IsNull() { 583 diags = diags.Append(errors.New("foo is not set")) 584 } 585 return providers.ValidateResourceConfigResponse{ 586 Diagnostics: diags, 587 } 588 } 589 590 diags := c.Validate() 591 if diags.HasErrors() { 592 t.Fatalf("unexpected error: %s", diags.Err()) 593 } 594 } 595 596 func TestContext2Validate_providerConfig_bad(t *testing.T) { 597 m := testModule(t, "validate-bad-pc") 598 p := testProvider("aws") 599 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 600 Provider: providers.Schema{ 601 Block: &configschema.Block{ 602 Attributes: map[string]*configschema.Attribute{ 603 "foo": {Type: cty.String, Optional: true}, 604 }, 605 }, 606 }, 607 ResourceTypes: map[string]providers.Schema{ 608 "aws_instance": { 609 Block: &configschema.Block{ 610 Attributes: map[string]*configschema.Attribute{}, 611 }, 612 }, 613 }, 614 } 615 616 c := testContext2(t, &ContextOpts{ 617 Config: m, 618 Providers: map[addrs.Provider]providers.Factory{ 619 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 620 }, 621 }) 622 623 p.ValidateProviderConfigResponse = &providers.ValidateProviderConfigResponse{ 624 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), 625 } 626 627 diags := c.Validate() 628 if len(diags) != 1 { 629 t.Fatalf("wrong number of diagnostics %d; want %d", len(diags), 1) 630 } 631 if !strings.Contains(diags.Err().Error(), "bad") { 632 t.Fatalf("bad: %s", diags.Err().Error()) 633 } 634 } 635 636 func TestContext2Validate_providerConfig_skippedEmpty(t *testing.T) { 637 m := testModule(t, "validate-skipped-pc-empty") 638 p := testProvider("aws") 639 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 640 Provider: providers.Schema{ 641 Block: &configschema.Block{ 642 Attributes: map[string]*configschema.Attribute{ 643 "foo": {Type: cty.String, Optional: true}, 644 }, 645 }, 646 }, 647 ResourceTypes: map[string]providers.Schema{ 648 "aws_instance": { 649 Block: &configschema.Block{ 650 Attributes: map[string]*configschema.Attribute{}, 651 }, 652 }, 653 }, 654 } 655 656 c := testContext2(t, &ContextOpts{ 657 Config: m, 658 Providers: map[addrs.Provider]providers.Factory{ 659 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 660 }, 661 }) 662 663 p.ValidateProviderConfigResponse = &providers.ValidateProviderConfigResponse{ 664 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("should not be called")), 665 } 666 667 diags := c.Validate() 668 if diags.HasErrors() { 669 t.Fatalf("unexpected error: %s", diags.Err()) 670 } 671 } 672 673 func TestContext2Validate_providerConfig_good(t *testing.T) { 674 m := testModule(t, "validate-bad-pc") 675 p := testProvider("aws") 676 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 677 Provider: providers.Schema{ 678 Block: &configschema.Block{ 679 Attributes: map[string]*configschema.Attribute{ 680 "foo": {Type: cty.String, Optional: true}, 681 }, 682 }, 683 }, 684 ResourceTypes: map[string]providers.Schema{ 685 "aws_instance": { 686 Block: &configschema.Block{ 687 Attributes: map[string]*configschema.Attribute{}, 688 }, 689 }, 690 }, 691 } 692 693 c := testContext2(t, &ContextOpts{ 694 Config: m, 695 Providers: map[addrs.Provider]providers.Factory{ 696 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 697 }, 698 }) 699 700 diags := c.Validate() 701 if diags.HasErrors() { 702 t.Fatalf("unexpected error: %s", diags.Err()) 703 } 704 } 705 706 // In this test there is a mismatch between the provider's fqn (hashicorp/test) 707 // and it's local name set in required_providers (arbitrary). 708 func TestContext2Validate_requiredProviderConfig(t *testing.T) { 709 m := testModule(t, "validate-required-provider-config") 710 p := testProvider("aws") 711 712 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 713 Provider: providers.Schema{ 714 Block: &configschema.Block{ 715 Attributes: map[string]*configschema.Attribute{ 716 "required_attribute": {Type: cty.String, Required: true}, 717 }, 718 }, 719 }, 720 ResourceTypes: map[string]providers.Schema{ 721 "aws_instance": { 722 Block: &configschema.Block{ 723 Attributes: map[string]*configschema.Attribute{}, 724 }, 725 }, 726 }, 727 } 728 729 c := testContext2(t, &ContextOpts{ 730 Config: m, 731 Providers: map[addrs.Provider]providers.Factory{ 732 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 733 }, 734 }) 735 736 diags := c.Validate() 737 if diags.HasErrors() { 738 t.Fatalf("unexpected error: %s", diags.Err()) 739 } 740 } 741 742 func TestContext2Validate_provisionerConfig_bad(t *testing.T) { 743 m := testModule(t, "validate-bad-prov-conf") 744 p := testProvider("aws") 745 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 746 ResourceTypes: map[string]providers.Schema{ 747 "aws_instance": { 748 Block: &configschema.Block{ 749 Attributes: map[string]*configschema.Attribute{ 750 "foo": {Type: cty.String, Optional: true}, 751 }, 752 }, 753 }, 754 }, 755 } 756 757 pr := simpleMockProvisioner() 758 759 c := testContext2(t, &ContextOpts{ 760 Config: m, 761 Providers: map[addrs.Provider]providers.Factory{ 762 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 763 }, 764 Provisioners: map[string]provisioners.Factory{ 765 "shell": testProvisionerFuncFixed(pr), 766 }, 767 }) 768 769 p.ValidateProviderConfigResponse = &providers.ValidateProviderConfigResponse{ 770 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), 771 } 772 773 diags := c.Validate() 774 if !diags.HasErrors() { 775 t.Fatalf("succeeded; want error") 776 } 777 } 778 779 func TestContext2Validate_badResourceConnection(t *testing.T) { 780 m := testModule(t, "validate-bad-resource-connection") 781 p := testProvider("aws") 782 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 783 ResourceTypes: map[string]providers.Schema{ 784 "aws_instance": { 785 Block: &configschema.Block{ 786 Attributes: map[string]*configschema.Attribute{ 787 "foo": {Type: cty.String, Optional: true}, 788 }, 789 }, 790 }, 791 }, 792 } 793 794 pr := simpleMockProvisioner() 795 796 c := testContext2(t, &ContextOpts{ 797 Config: m, 798 Providers: map[addrs.Provider]providers.Factory{ 799 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 800 }, 801 Provisioners: map[string]provisioners.Factory{ 802 "shell": testProvisionerFuncFixed(pr), 803 }, 804 }) 805 806 diags := c.Validate() 807 t.Log(diags.Err()) 808 if !diags.HasErrors() { 809 t.Fatalf("succeeded; want error") 810 } 811 } 812 813 func TestContext2Validate_badProvisionerConnection(t *testing.T) { 814 m := testModule(t, "validate-bad-prov-connection") 815 p := testProvider("aws") 816 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 817 ResourceTypes: map[string]providers.Schema{ 818 "aws_instance": { 819 Block: &configschema.Block{ 820 Attributes: map[string]*configschema.Attribute{ 821 "foo": {Type: cty.String, Optional: true}, 822 }, 823 }, 824 }, 825 }, 826 } 827 828 pr := simpleMockProvisioner() 829 830 c := testContext2(t, &ContextOpts{ 831 Config: m, 832 Providers: map[addrs.Provider]providers.Factory{ 833 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 834 }, 835 Provisioners: map[string]provisioners.Factory{ 836 "shell": testProvisionerFuncFixed(pr), 837 }, 838 }) 839 840 diags := c.Validate() 841 t.Log(diags.Err()) 842 if !diags.HasErrors() { 843 t.Fatalf("succeeded; want error") 844 } 845 } 846 847 func TestContext2Validate_provisionerConfig_good(t *testing.T) { 848 m := testModule(t, "validate-bad-prov-conf") 849 p := testProvider("aws") 850 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 851 Provider: providers.Schema{ 852 Block: &configschema.Block{ 853 Attributes: map[string]*configschema.Attribute{ 854 "foo": {Type: cty.String, Optional: true}, 855 }, 856 }, 857 }, 858 ResourceTypes: map[string]providers.Schema{ 859 "aws_instance": { 860 Block: &configschema.Block{ 861 Attributes: map[string]*configschema.Attribute{ 862 "foo": {Type: cty.String, Optional: true}, 863 }, 864 }, 865 }, 866 }, 867 } 868 869 pr := simpleMockProvisioner() 870 pr.ValidateProvisionerConfigFn = func(req provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse { 871 var diags tfdiags.Diagnostics 872 if req.Config.GetAttr("test_string").IsNull() { 873 diags = diags.Append(errors.New("test_string is not set")) 874 } 875 return provisioners.ValidateProvisionerConfigResponse{ 876 Diagnostics: diags, 877 } 878 } 879 880 c := testContext2(t, &ContextOpts{ 881 Config: m, 882 Providers: map[addrs.Provider]providers.Factory{ 883 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 884 }, 885 Provisioners: map[string]provisioners.Factory{ 886 "shell": testProvisionerFuncFixed(pr), 887 }, 888 }) 889 890 diags := c.Validate() 891 if diags.HasErrors() { 892 t.Fatalf("unexpected error: %s", diags.Err()) 893 } 894 } 895 896 func TestContext2Validate_requiredVar(t *testing.T) { 897 m := testModule(t, "validate-required-var") 898 p := testProvider("aws") 899 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 900 ResourceTypes: map[string]providers.Schema{ 901 "aws_instance": { 902 Block: &configschema.Block{ 903 Attributes: map[string]*configschema.Attribute{ 904 "ami": {Type: cty.String, Optional: true}, 905 }, 906 }, 907 }, 908 }, 909 } 910 _, diags := NewContext(&ContextOpts{ 911 Config: m, 912 Providers: map[addrs.Provider]providers.Factory{ 913 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 914 }, 915 }) 916 if !diags.HasErrors() { 917 // Error should be: The input variable "foo" has not been assigned a value. 918 t.Fatalf("succeeded; want error") 919 } 920 } 921 922 func TestContext2Validate_resourceConfig_bad(t *testing.T) { 923 m := testModule(t, "validate-bad-rc") 924 p := testProvider("aws") 925 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 926 ResourceTypes: map[string]providers.Schema{ 927 "aws_instance": { 928 Block: &configschema.Block{ 929 Attributes: map[string]*configschema.Attribute{ 930 "foo": {Type: cty.String, Optional: true}, 931 }, 932 }, 933 }, 934 }, 935 } 936 c := testContext2(t, &ContextOpts{ 937 Config: m, 938 Providers: map[addrs.Provider]providers.Factory{ 939 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 940 }, 941 }) 942 943 p.ValidateResourceConfigResponse = &providers.ValidateResourceConfigResponse{ 944 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), 945 } 946 947 diags := c.Validate() 948 if !diags.HasErrors() { 949 t.Fatalf("succeeded; want error") 950 } 951 } 952 953 func TestContext2Validate_resourceConfig_good(t *testing.T) { 954 m := testModule(t, "validate-bad-rc") 955 p := testProvider("aws") 956 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 957 ResourceTypes: map[string]providers.Schema{ 958 "aws_instance": { 959 Block: &configschema.Block{ 960 Attributes: map[string]*configschema.Attribute{ 961 "foo": {Type: cty.String, Optional: true}, 962 }, 963 }, 964 }, 965 }, 966 } 967 c := testContext2(t, &ContextOpts{ 968 Config: m, 969 Providers: map[addrs.Provider]providers.Factory{ 970 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 971 }, 972 }) 973 974 diags := c.Validate() 975 if diags.HasErrors() { 976 t.Fatalf("unexpected error: %s", diags.Err()) 977 } 978 } 979 980 func TestContext2Validate_tainted(t *testing.T) { 981 p := testProvider("aws") 982 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 983 ResourceTypes: map[string]providers.Schema{ 984 "aws_instance": { 985 Block: &configschema.Block{ 986 Attributes: map[string]*configschema.Attribute{ 987 "foo": {Type: cty.String, Optional: true}, 988 "num": {Type: cty.String, Optional: true}, 989 }, 990 }, 991 }, 992 }, 993 } 994 995 m := testModule(t, "validate-good") 996 state := states.NewState() 997 root := state.EnsureModule(addrs.RootModuleInstance) 998 testSetResourceInstanceTainted(root, "aws_instance.foo", `{"id":"bar"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 999 1000 c := testContext2(t, &ContextOpts{ 1001 Config: m, 1002 Providers: map[addrs.Provider]providers.Factory{ 1003 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1004 }, 1005 State: state, 1006 }) 1007 1008 p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { 1009 var diags tfdiags.Diagnostics 1010 if req.Config.GetAttr("foo").IsNull() { 1011 diags = diags.Append(errors.New("foo is not set")) 1012 } 1013 return providers.ValidateResourceConfigResponse{ 1014 Diagnostics: diags, 1015 } 1016 } 1017 1018 diags := c.Validate() 1019 if diags.HasErrors() { 1020 t.Fatalf("unexpected error: %s", diags.Err()) 1021 } 1022 } 1023 1024 func TestContext2Validate_targetedDestroy(t *testing.T) { 1025 m := testModule(t, "validate-targeted") 1026 p := testProvider("aws") 1027 pr := simpleMockProvisioner() 1028 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 1029 ResourceTypes: map[string]providers.Schema{ 1030 "aws_instance": { 1031 Block: &configschema.Block{ 1032 Attributes: map[string]*configschema.Attribute{ 1033 "foo": {Type: cty.String, Optional: true}, 1034 "num": {Type: cty.String, Optional: true}, 1035 }, 1036 }, 1037 }, 1038 }, 1039 } 1040 1041 state := states.NewState() 1042 root := state.EnsureModule(addrs.RootModuleInstance) 1043 testSetResourceInstanceCurrent(root, "aws_instance.foo", `{"id":"i-bcd345"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 1044 testSetResourceInstanceCurrent(root, "aws_instance.bar", `{"id":"i-abc123"}`, `provider["registry.terraform.io/hashicorp/aws"]`) 1045 1046 ctx := testContext2(t, &ContextOpts{ 1047 Config: m, 1048 Providers: map[addrs.Provider]providers.Factory{ 1049 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1050 }, 1051 Provisioners: map[string]provisioners.Factory{ 1052 "shell": testProvisionerFuncFixed(pr), 1053 }, 1054 State: state, 1055 Targets: []addrs.Targetable{ 1056 addrs.RootModuleInstance.Resource( 1057 addrs.ManagedResourceMode, "aws_instance", "foo", 1058 ), 1059 }, 1060 PlanMode: plans.DestroyMode, 1061 }) 1062 1063 diags := ctx.Validate() 1064 if diags.HasErrors() { 1065 t.Fatalf("unexpected error: %s", diags.Err()) 1066 } 1067 } 1068 1069 func TestContext2Validate_varRefUnknown(t *testing.T) { 1070 m := testModule(t, "validate-variable-ref") 1071 p := testProvider("aws") 1072 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 1073 ResourceTypes: map[string]providers.Schema{ 1074 "aws_instance": { 1075 Block: &configschema.Block{ 1076 Attributes: map[string]*configschema.Attribute{ 1077 "foo": {Type: cty.String, Optional: true}, 1078 }, 1079 }, 1080 }, 1081 }, 1082 } 1083 c := testContext2(t, &ContextOpts{ 1084 Config: m, 1085 Providers: map[addrs.Provider]providers.Factory{ 1086 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1087 }, 1088 Variables: InputValues{ 1089 "foo": &InputValue{ 1090 Value: cty.StringVal("bar"), 1091 SourceType: ValueFromCaller, 1092 }, 1093 }, 1094 }) 1095 1096 var value cty.Value 1097 p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { 1098 value = req.Config.GetAttr("foo") 1099 return providers.ValidateResourceConfigResponse{} 1100 } 1101 1102 c.Validate() 1103 1104 // Input variables are always unknown during the validate walk, because 1105 // we're checking for validity of all possible input values. Validity 1106 // against specific input values is checked during the plan walk. 1107 if !value.RawEquals(cty.UnknownVal(cty.String)) { 1108 t.Fatalf("bad: %#v", value) 1109 } 1110 } 1111 1112 // Module variables weren't being interpolated during Validate phase. 1113 // related to https://github.com/iaas-resource-provision/iaas-rpc/issues/5322 1114 func TestContext2Validate_interpolateVar(t *testing.T) { 1115 input := new(MockUIInput) 1116 1117 m := testModule(t, "input-interpolate-var") 1118 p := testProvider("null") 1119 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 1120 ResourceTypes: map[string]providers.Schema{ 1121 "template_file": { 1122 Block: &configschema.Block{ 1123 Attributes: map[string]*configschema.Attribute{ 1124 "template": {Type: cty.String, Optional: true}, 1125 }, 1126 }, 1127 }, 1128 }, 1129 } 1130 1131 ctx := testContext2(t, &ContextOpts{ 1132 Config: m, 1133 Providers: map[addrs.Provider]providers.Factory{ 1134 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 1135 }, 1136 UIInput: input, 1137 }) 1138 1139 diags := ctx.Validate() 1140 if diags.HasErrors() { 1141 t.Fatalf("unexpected error: %s", diags.Err()) 1142 } 1143 } 1144 1145 // When module vars reference something that is actually computed, this 1146 // shouldn't cause validation to fail. 1147 func TestContext2Validate_interpolateComputedModuleVarDef(t *testing.T) { 1148 input := new(MockUIInput) 1149 1150 m := testModule(t, "validate-computed-module-var-ref") 1151 p := testProvider("aws") 1152 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 1153 ResourceTypes: map[string]providers.Schema{ 1154 "aws_instance": { 1155 Block: &configschema.Block{ 1156 Attributes: map[string]*configschema.Attribute{ 1157 "attr": {Type: cty.String, Optional: true}, 1158 }, 1159 }, 1160 }, 1161 }, 1162 } 1163 1164 ctx := testContext2(t, &ContextOpts{ 1165 Config: m, 1166 Providers: map[addrs.Provider]providers.Factory{ 1167 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1168 }, 1169 UIInput: input, 1170 }) 1171 1172 diags := ctx.Validate() 1173 if diags.HasErrors() { 1174 t.Fatalf("unexpected error: %s", diags.Err()) 1175 } 1176 } 1177 1178 // Computed values are lost when a map is output from a module 1179 func TestContext2Validate_interpolateMap(t *testing.T) { 1180 input := new(MockUIInput) 1181 1182 m := testModule(t, "issue-9549") 1183 p := testProvider("template") 1184 1185 ctx := testContext2(t, &ContextOpts{ 1186 Config: m, 1187 Providers: map[addrs.Provider]providers.Factory{ 1188 addrs.NewDefaultProvider("template"): testProviderFuncFixed(p), 1189 }, 1190 UIInput: input, 1191 }) 1192 1193 diags := ctx.Validate() 1194 if diags.HasErrors() { 1195 t.Fatalf("unexpected error: %s", diags.Err()) 1196 } 1197 } 1198 1199 func TestContext2Validate_varSensitive(t *testing.T) { 1200 // Smoke test through validate where a variable has sensitive applied 1201 m := testModuleInline(t, map[string]string{ 1202 "main.tf": ` 1203 variable "foo" { 1204 default = "xyz" 1205 sensitive = true 1206 } 1207 1208 variable "bar" { 1209 sensitive = true 1210 } 1211 1212 data "aws_data_source" "bar" { 1213 foo = var.bar 1214 } 1215 1216 resource "aws_instance" "foo" { 1217 foo = var.foo 1218 } 1219 `, 1220 }) 1221 1222 p := testProvider("aws") 1223 p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { 1224 // Providers receive unmarked values 1225 if got, want := req.Config.GetAttr("foo"), cty.UnknownVal(cty.String); !got.RawEquals(want) { 1226 t.Fatalf("wrong value for foo\ngot: %#v\nwant: %#v", got, want) 1227 } 1228 return providers.ValidateResourceConfigResponse{} 1229 } 1230 p.ValidateDataResourceConfigFn = func(req providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { 1231 if got, want := req.Config.GetAttr("foo"), cty.UnknownVal(cty.String); !got.RawEquals(want) { 1232 t.Fatalf("wrong value for foo\ngot: %#v\nwant: %#v", got, want) 1233 } 1234 return providers.ValidateDataResourceConfigResponse{} 1235 } 1236 1237 ctx := testContext2(t, &ContextOpts{ 1238 Config: m, 1239 Providers: map[addrs.Provider]providers.Factory{ 1240 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1241 }, 1242 Variables: InputValues{ 1243 "bar": &InputValue{ 1244 Value: cty.StringVal("boop"), 1245 SourceType: ValueFromCaller, 1246 }, 1247 }, 1248 }) 1249 1250 diags := ctx.Validate() 1251 if diags.HasErrors() { 1252 t.Fatal(diags.Err()) 1253 } 1254 1255 if !p.ValidateResourceConfigCalled { 1256 t.Fatal("expected ValidateResourceConfigFn to be called") 1257 } 1258 1259 if !p.ValidateDataResourceConfigCalled { 1260 t.Fatal("expected ValidateDataSourceConfigFn to be called") 1261 } 1262 } 1263 1264 // Manually validate using the new PlanGraphBuilder 1265 func TestContext2Validate_PlanGraphBuilder(t *testing.T) { 1266 fixture := contextFixtureApplyVars(t) 1267 opts := fixture.ContextOpts() 1268 opts.Variables = InputValues{ 1269 "foo": &InputValue{ 1270 Value: cty.StringVal("us-east-1"), 1271 SourceType: ValueFromCaller, 1272 }, 1273 "test_list": &InputValue{ 1274 Value: cty.ListVal([]cty.Value{ 1275 cty.StringVal("Hello"), 1276 cty.StringVal("World"), 1277 }), 1278 SourceType: ValueFromCaller, 1279 }, 1280 "test_map": &InputValue{ 1281 Value: cty.MapVal(map[string]cty.Value{ 1282 "Hello": cty.StringVal("World"), 1283 "Foo": cty.StringVal("Bar"), 1284 "Baz": cty.StringVal("Foo"), 1285 }), 1286 SourceType: ValueFromCaller, 1287 }, 1288 "amis": &InputValue{ 1289 Value: cty.MapVal(map[string]cty.Value{ 1290 "us-east-1": cty.StringVal("override"), 1291 }), 1292 SourceType: ValueFromCaller, 1293 }, 1294 } 1295 c := testContext2(t, opts) 1296 1297 graph, diags := (&PlanGraphBuilder{ 1298 Config: c.config, 1299 State: states.NewState(), 1300 Components: c.components, 1301 Schemas: c.schemas, 1302 Targets: c.targets, 1303 }).Build(addrs.RootModuleInstance) 1304 if diags.HasErrors() { 1305 t.Fatalf("errors from PlanGraphBuilder: %s", diags.Err()) 1306 } 1307 defer c.acquireRun("validate-test")() 1308 walker, diags := c.walk(graph, walkValidate) 1309 if diags.HasErrors() { 1310 t.Fatal(diags.Err()) 1311 } 1312 if len(walker.NonFatalDiagnostics) > 0 { 1313 t.Fatal(walker.NonFatalDiagnostics.Err()) 1314 } 1315 } 1316 1317 func TestContext2Validate_invalidOutput(t *testing.T) { 1318 m := testModuleInline(t, map[string]string{ 1319 "main.tf": ` 1320 data "aws_data_source" "name" {} 1321 1322 output "out" { 1323 value = "${data.aws_data_source.name.missing}" 1324 }`, 1325 }) 1326 1327 p := testProvider("aws") 1328 ctx := testContext2(t, &ContextOpts{ 1329 Config: m, 1330 Providers: map[addrs.Provider]providers.Factory{ 1331 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1332 }, 1333 }) 1334 1335 diags := ctx.Validate() 1336 if !diags.HasErrors() { 1337 t.Fatal("succeeded; want errors") 1338 } 1339 // Should get this error: 1340 // Unsupported attribute: This object does not have an attribute named "missing" 1341 if got, want := diags.Err().Error(), "Unsupported attribute"; !strings.Contains(got, want) { 1342 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1343 } 1344 } 1345 1346 func TestContext2Validate_invalidModuleOutput(t *testing.T) { 1347 m := testModuleInline(t, map[string]string{ 1348 "child/main.tf": ` 1349 data "aws_data_source" "name" {} 1350 1351 output "out" { 1352 value = "${data.aws_data_source.name.missing}" 1353 }`, 1354 "main.tf": ` 1355 module "child" { 1356 source = "./child" 1357 } 1358 1359 resource "aws_instance" "foo" { 1360 foo = "${module.child.out}" 1361 }`, 1362 }) 1363 1364 p := testProvider("aws") 1365 ctx := testContext2(t, &ContextOpts{ 1366 Config: m, 1367 Providers: map[addrs.Provider]providers.Factory{ 1368 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1369 }, 1370 }) 1371 1372 diags := ctx.Validate() 1373 if !diags.HasErrors() { 1374 t.Fatal("succeeded; want errors") 1375 } 1376 // Should get this error: 1377 // Unsupported attribute: This object does not have an attribute named "missing" 1378 if got, want := diags.Err().Error(), "Unsupported attribute"; !strings.Contains(got, want) { 1379 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1380 } 1381 } 1382 1383 func TestContext2Validate_sensitiveRootModuleOutput(t *testing.T) { 1384 m := testModuleInline(t, map[string]string{ 1385 "child/main.tf": ` 1386 variable "foo" { 1387 default = "xyz" 1388 sensitive = true 1389 } 1390 1391 output "out" { 1392 value = var.foo 1393 }`, 1394 "main.tf": ` 1395 module "child" { 1396 source = "./child" 1397 } 1398 1399 output "root" { 1400 value = module.child.out 1401 sensitive = true 1402 }`, 1403 }) 1404 1405 ctx := testContext2(t, &ContextOpts{ 1406 Config: m, 1407 }) 1408 1409 diags := ctx.Validate() 1410 if diags.HasErrors() { 1411 t.Fatal(diags.Err()) 1412 } 1413 } 1414 1415 func TestContext2Validate_legacyResourceCount(t *testing.T) { 1416 m := testModuleInline(t, map[string]string{ 1417 "main.tf": ` 1418 resource "aws_instance" "test" {} 1419 1420 output "out" { 1421 value = aws_instance.test.count 1422 }`, 1423 }) 1424 1425 p := testProvider("aws") 1426 ctx := testContext2(t, &ContextOpts{ 1427 Config: m, 1428 Providers: map[addrs.Provider]providers.Factory{ 1429 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1430 }, 1431 }) 1432 1433 diags := ctx.Validate() 1434 if !diags.HasErrors() { 1435 t.Fatal("succeeded; want errors") 1436 } 1437 // Should get this error: 1438 // 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. 1439 if got, want := diags.Err().Error(), "Invalid resource count attribute:"; !strings.Contains(got, want) { 1440 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1441 } 1442 } 1443 1444 func TestContext2Validate_invalidModuleRef(t *testing.T) { 1445 // This test is verifying that we properly validate and report on references 1446 // to modules that are not declared, since we were missing some validation 1447 // here in early 0.12.0 alphas that led to a panic. 1448 m := testModuleInline(t, map[string]string{ 1449 "main.tf": ` 1450 output "out" { 1451 # Intentionally referencing undeclared module to ensure error 1452 value = module.foo 1453 }`, 1454 }) 1455 1456 p := testProvider("aws") 1457 ctx := testContext2(t, &ContextOpts{ 1458 Config: m, 1459 Providers: map[addrs.Provider]providers.Factory{ 1460 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1461 }, 1462 }) 1463 1464 diags := ctx.Validate() 1465 if !diags.HasErrors() { 1466 t.Fatal("succeeded; want errors") 1467 } 1468 // Should get this error: 1469 // Reference to undeclared module: No module call named "foo" is declared in the root module. 1470 if got, want := diags.Err().Error(), "Reference to undeclared module:"; !strings.Contains(got, want) { 1471 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1472 } 1473 } 1474 1475 func TestContext2Validate_invalidModuleOutputRef(t *testing.T) { 1476 // This test is verifying that we properly validate and report on references 1477 // to modules that are not declared, since we were missing some validation 1478 // here in early 0.12.0 alphas that led to a panic. 1479 m := testModuleInline(t, map[string]string{ 1480 "main.tf": ` 1481 output "out" { 1482 # Intentionally referencing undeclared module to ensure error 1483 value = module.foo.bar 1484 }`, 1485 }) 1486 1487 p := testProvider("aws") 1488 ctx := testContext2(t, &ContextOpts{ 1489 Config: m, 1490 Providers: map[addrs.Provider]providers.Factory{ 1491 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1492 }, 1493 }) 1494 1495 diags := ctx.Validate() 1496 if !diags.HasErrors() { 1497 t.Fatal("succeeded; want errors") 1498 } 1499 // Should get this error: 1500 // Reference to undeclared module: No module call named "foo" is declared in the root module. 1501 if got, want := diags.Err().Error(), "Reference to undeclared module:"; !strings.Contains(got, want) { 1502 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1503 } 1504 } 1505 1506 func TestContext2Validate_invalidDependsOnResourceRef(t *testing.T) { 1507 // This test is verifying that we raise an error if depends_on 1508 // refers to something that doesn't exist in configuration. 1509 m := testModuleInline(t, map[string]string{ 1510 "main.tf": ` 1511 resource "test_instance" "bar" { 1512 depends_on = [test_resource.nonexistant] 1513 } 1514 `, 1515 }) 1516 1517 p := testProvider("test") 1518 ctx := testContext2(t, &ContextOpts{ 1519 Config: m, 1520 Providers: map[addrs.Provider]providers.Factory{ 1521 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 1522 }, 1523 }) 1524 1525 diags := ctx.Validate() 1526 if !diags.HasErrors() { 1527 t.Fatal("succeeded; want errors") 1528 } 1529 // Should get this error: 1530 // Reference to undeclared module: No module call named "foo" is declared in the root module. 1531 if got, want := diags.Err().Error(), "Reference to undeclared resource:"; !strings.Contains(got, want) { 1532 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1533 } 1534 } 1535 1536 func TestContext2Validate_invalidResourceIgnoreChanges(t *testing.T) { 1537 // This test is verifying that we raise an error if ignore_changes 1538 // refers to something that can be statically detected as not conforming 1539 // to the resource type schema. 1540 m := testModuleInline(t, map[string]string{ 1541 "main.tf": ` 1542 resource "test_instance" "bar" { 1543 lifecycle { 1544 ignore_changes = [does_not_exist_in_schema] 1545 } 1546 } 1547 `, 1548 }) 1549 1550 p := testProvider("test") 1551 ctx := testContext2(t, &ContextOpts{ 1552 Config: m, 1553 Providers: map[addrs.Provider]providers.Factory{ 1554 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 1555 }, 1556 }) 1557 1558 diags := ctx.Validate() 1559 if !diags.HasErrors() { 1560 t.Fatal("succeeded; want errors") 1561 } 1562 // Should get this error: 1563 // Reference to undeclared module: No module call named "foo" is declared in the root module. 1564 if got, want := diags.Err().Error(), `no argument, nested block, or exported attribute named "does_not_exist_in_schema"`; !strings.Contains(got, want) { 1565 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1566 } 1567 } 1568 1569 func TestContext2Validate_variableCustomValidationsFail(t *testing.T) { 1570 // This test is for custom validation rules associated with root module 1571 // variables, and specifically that we handle the situation where the 1572 // given value is invalid in a child module. 1573 m := testModule(t, "validate-variable-custom-validations-child") 1574 1575 p := testProvider("test") 1576 ctx := testContext2(t, &ContextOpts{ 1577 Config: m, 1578 Providers: map[addrs.Provider]providers.Factory{ 1579 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 1580 }, 1581 }) 1582 1583 diags := ctx.Validate() 1584 if !diags.HasErrors() { 1585 t.Fatal("succeeded; want errors") 1586 } 1587 if got, want := diags.Err().Error(), `Invalid value for variable: Value must not be "nope".`; !strings.Contains(got, want) { 1588 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1589 } 1590 } 1591 1592 func TestContext2Validate_variableCustomValidationsRoot(t *testing.T) { 1593 // This test is for custom validation rules associated with root module 1594 // variables, and specifically that we handle the situation where their 1595 // values are unknown during validation, skipping the validation check 1596 // altogether. (Root module variables are never known during validation.) 1597 m := testModuleInline(t, map[string]string{ 1598 "main.tf": ` 1599 variable "test" { 1600 type = string 1601 1602 validation { 1603 condition = var.test != "nope" 1604 error_message = "Value must not be \"nope\"." 1605 } 1606 } 1607 `, 1608 }) 1609 1610 p := testProvider("test") 1611 ctx := testContext2(t, &ContextOpts{ 1612 Config: m, 1613 Providers: map[addrs.Provider]providers.Factory{ 1614 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 1615 }, 1616 Variables: InputValues{ 1617 "test": &InputValue{ 1618 Value: cty.UnknownVal(cty.String), 1619 SourceType: ValueFromCLIArg, 1620 }, 1621 }, 1622 }) 1623 1624 diags := ctx.Validate() 1625 if diags.HasErrors() { 1626 t.Fatalf("unexpected error\ngot: %s", diags.Err().Error()) 1627 } 1628 } 1629 1630 func TestContext2Validate_expandModules(t *testing.T) { 1631 m := testModuleInline(t, map[string]string{ 1632 "main.tf": ` 1633 module "mod1" { 1634 for_each = toset(["a", "b"]) 1635 source = "./mod" 1636 } 1637 1638 module "mod2" { 1639 for_each = module.mod1 1640 source = "./mod" 1641 input = module.mod1["a"].out 1642 } 1643 1644 module "mod3" { 1645 count = length(module.mod2) 1646 source = "./mod" 1647 } 1648 `, 1649 "mod/main.tf": ` 1650 resource "aws_instance" "foo" { 1651 } 1652 1653 output "out" { 1654 value = 1 1655 } 1656 1657 variable "input" { 1658 type = number 1659 default = 0 1660 } 1661 1662 module "nested" { 1663 count = 2 1664 source = "./nested" 1665 input = count.index 1666 } 1667 `, 1668 "mod/nested/main.tf": ` 1669 variable "input" { 1670 } 1671 1672 resource "aws_instance" "foo" { 1673 count = var.input 1674 } 1675 `, 1676 }) 1677 1678 p := testProvider("aws") 1679 ctx := testContext2(t, &ContextOpts{ 1680 Config: m, 1681 Providers: map[addrs.Provider]providers.Factory{ 1682 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1683 }, 1684 }) 1685 1686 diags := ctx.Validate() 1687 if diags.HasErrors() { 1688 t.Fatal(diags.ErrWithWarnings()) 1689 } 1690 } 1691 1692 func TestContext2Validate_expandModulesInvalidCount(t *testing.T) { 1693 m := testModuleInline(t, map[string]string{ 1694 "main.tf": ` 1695 module "mod1" { 1696 count = -1 1697 source = "./mod" 1698 } 1699 `, 1700 "mod/main.tf": ` 1701 resource "aws_instance" "foo" { 1702 } 1703 `, 1704 }) 1705 1706 p := testProvider("aws") 1707 ctx := testContext2(t, &ContextOpts{ 1708 Config: m, 1709 Providers: map[addrs.Provider]providers.Factory{ 1710 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1711 }, 1712 }) 1713 1714 diags := ctx.Validate() 1715 if !diags.HasErrors() { 1716 t.Fatal("succeeded; want errors") 1717 } 1718 if got, want := diags.Err().Error(), `Invalid count argument`; !strings.Contains(got, want) { 1719 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1720 } 1721 } 1722 1723 func TestContext2Validate_expandModulesInvalidForEach(t *testing.T) { 1724 m := testModuleInline(t, map[string]string{ 1725 "main.tf": ` 1726 module "mod1" { 1727 for_each = ["a", "b"] 1728 source = "./mod" 1729 } 1730 `, 1731 "mod/main.tf": ` 1732 resource "aws_instance" "foo" { 1733 } 1734 `, 1735 }) 1736 1737 p := testProvider("aws") 1738 ctx := testContext2(t, &ContextOpts{ 1739 Config: m, 1740 Providers: map[addrs.Provider]providers.Factory{ 1741 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1742 }, 1743 }) 1744 1745 diags := ctx.Validate() 1746 if !diags.HasErrors() { 1747 t.Fatal("succeeded; want errors") 1748 } 1749 if got, want := diags.Err().Error(), `Invalid for_each argument`; !strings.Contains(got, want) { 1750 t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) 1751 } 1752 } 1753 1754 func TestContext2Validate_expandMultipleNestedModules(t *testing.T) { 1755 m := testModuleInline(t, map[string]string{ 1756 "main.tf": ` 1757 module "modA" { 1758 for_each = { 1759 first = "m" 1760 second = "n" 1761 } 1762 source = "./modA" 1763 } 1764 `, 1765 "modA/main.tf": ` 1766 locals { 1767 m = { 1768 first = "m" 1769 second = "n" 1770 } 1771 } 1772 1773 module "modB" { 1774 for_each = local.m 1775 source = "./modB" 1776 y = each.value 1777 } 1778 1779 module "modC" { 1780 for_each = local.m 1781 source = "./modC" 1782 x = module.modB[each.key].out 1783 y = module.modB[each.key].out 1784 } 1785 1786 `, 1787 "modA/modB/main.tf": ` 1788 variable "y" { 1789 type = string 1790 } 1791 1792 resource "aws_instance" "foo" { 1793 foo = var.y 1794 } 1795 1796 output "out" { 1797 value = aws_instance.foo.id 1798 } 1799 `, 1800 "modA/modC/main.tf": ` 1801 variable "x" { 1802 type = string 1803 } 1804 1805 variable "y" { 1806 type = string 1807 } 1808 1809 resource "aws_instance" "foo" { 1810 foo = var.x 1811 } 1812 1813 output "out" { 1814 value = var.y 1815 } 1816 `, 1817 }) 1818 1819 p := testProvider("aws") 1820 ctx := testContext2(t, &ContextOpts{ 1821 Config: m, 1822 Providers: map[addrs.Provider]providers.Factory{ 1823 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1824 }, 1825 }) 1826 1827 diags := ctx.Validate() 1828 if diags.HasErrors() { 1829 t.Fatal(diags.ErrWithWarnings()) 1830 } 1831 } 1832 1833 func TestContext2Validate_invalidModuleDependsOn(t *testing.T) { 1834 // validate module and output depends_on 1835 m := testModuleInline(t, map[string]string{ 1836 "main.tf": ` 1837 module "mod1" { 1838 source = "./mod" 1839 depends_on = [resource_foo.bar.baz] 1840 } 1841 1842 module "mod2" { 1843 source = "./mod" 1844 depends_on = [resource_foo.bar.baz] 1845 } 1846 `, 1847 "mod/main.tf": ` 1848 output "out" { 1849 value = "foo" 1850 } 1851 `, 1852 }) 1853 1854 diags := testContext2(t, &ContextOpts{ 1855 Config: m, 1856 }).Validate() 1857 if !diags.HasErrors() { 1858 t.Fatal("succeeded; want errors") 1859 } 1860 1861 if len(diags) != 2 { 1862 t.Fatalf("wanted 2 diagnostic errors, got %q", diags) 1863 } 1864 1865 for _, d := range diags { 1866 des := d.Description().Summary 1867 if !strings.Contains(des, "Invalid depends_on reference") { 1868 t.Fatalf(`expected "Invalid depends_on reference", got %q`, des) 1869 } 1870 } 1871 } 1872 1873 func TestContext2Validate_invalidOutputDependsOn(t *testing.T) { 1874 // validate module and output depends_on 1875 m := testModuleInline(t, map[string]string{ 1876 "main.tf": ` 1877 module "mod1" { 1878 source = "./mod" 1879 } 1880 1881 output "out" { 1882 value = "bar" 1883 depends_on = [resource_foo.bar.baz] 1884 } 1885 `, 1886 "mod/main.tf": ` 1887 output "out" { 1888 value = "bar" 1889 depends_on = [resource_foo.bar.baz] 1890 } 1891 `, 1892 }) 1893 1894 diags := testContext2(t, &ContextOpts{ 1895 Config: m, 1896 }).Validate() 1897 if !diags.HasErrors() { 1898 t.Fatal("succeeded; want errors") 1899 } 1900 1901 if len(diags) != 2 { 1902 t.Fatalf("wanted 2 diagnostic errors, got %q", diags) 1903 } 1904 1905 for _, d := range diags { 1906 des := d.Description().Summary 1907 if !strings.Contains(des, "Invalid depends_on reference") { 1908 t.Fatalf(`expected "Invalid depends_on reference", got %q`, des) 1909 } 1910 } 1911 } 1912 1913 func TestContext2Validate_rpcDiagnostics(t *testing.T) { 1914 // validate module and output depends_on 1915 m := testModuleInline(t, map[string]string{ 1916 "main.tf": ` 1917 resource "test_instance" "a" { 1918 } 1919 `, 1920 }) 1921 1922 p := testProvider("test") 1923 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 1924 ResourceTypes: map[string]providers.Schema{ 1925 "test_instance": { 1926 Block: &configschema.Block{ 1927 Attributes: map[string]*configschema.Attribute{ 1928 "id": {Type: cty.String, Computed: true}, 1929 }, 1930 }, 1931 }, 1932 }, 1933 } 1934 1935 p.ValidateResourceConfigResponse = &providers.ValidateResourceConfigResponse{ 1936 Diagnostics: tfdiags.Diagnostics(nil).Append(tfdiags.SimpleWarning("don't frobble")), 1937 } 1938 1939 ctx := testContext2(t, &ContextOpts{ 1940 Config: m, 1941 Providers: map[addrs.Provider]providers.Factory{ 1942 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 1943 }, 1944 }) 1945 diags := ctx.Validate() 1946 if diags.HasErrors() { 1947 t.Fatal(diags.Err()) 1948 } 1949 1950 if len(diags) == 0 { 1951 t.Fatal("expected warnings") 1952 } 1953 1954 for _, d := range diags { 1955 des := d.Description().Summary 1956 if !strings.Contains(des, "frobble") { 1957 t.Fatalf(`expected frobble, got %q`, des) 1958 } 1959 } 1960 } 1961 1962 func TestContext2Validate_sensitiveProvisionerConfig(t *testing.T) { 1963 m := testModule(t, "validate-sensitive-provisioner-config") 1964 p := testProvider("aws") 1965 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 1966 ResourceTypes: map[string]providers.Schema{ 1967 "aws_instance": { 1968 Block: &configschema.Block{ 1969 Attributes: map[string]*configschema.Attribute{ 1970 "foo": {Type: cty.String, Optional: true}, 1971 }, 1972 }, 1973 }, 1974 }, 1975 } 1976 1977 pr := simpleMockProvisioner() 1978 1979 c := testContext2(t, &ContextOpts{ 1980 Config: m, 1981 Providers: map[addrs.Provider]providers.Factory{ 1982 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 1983 }, 1984 Provisioners: map[string]provisioners.Factory{ 1985 "test": testProvisionerFuncFixed(pr), 1986 }, 1987 }) 1988 1989 pr.ValidateProvisionerConfigFn = func(r provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse { 1990 if r.Config.ContainsMarked() { 1991 t.Errorf("provisioner config contains marked values") 1992 } 1993 return pr.ValidateProvisionerConfigResponse 1994 } 1995 1996 diags := c.Validate() 1997 if diags.HasErrors() { 1998 t.Fatalf("unexpected error: %s", diags.Err()) 1999 } 2000 if !pr.ValidateProvisionerConfigCalled { 2001 t.Fatal("ValidateProvisionerConfig not called") 2002 } 2003 } 2004 2005 func TestContext2Plan_validateMinMaxDynamicBlock(t *testing.T) { 2006 p := new(MockProvider) 2007 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 2008 ResourceTypes: map[string]*configschema.Block{ 2009 "test_instance": { 2010 Attributes: map[string]*configschema.Attribute{ 2011 "id": { 2012 Type: cty.String, 2013 Computed: true, 2014 }, 2015 "things": { 2016 Type: cty.List(cty.String), 2017 Computed: true, 2018 }, 2019 }, 2020 BlockTypes: map[string]*configschema.NestedBlock{ 2021 "foo": { 2022 Block: configschema.Block{ 2023 Attributes: map[string]*configschema.Attribute{ 2024 "bar": {Type: cty.String, Optional: true}, 2025 }, 2026 }, 2027 Nesting: configschema.NestingList, 2028 MinItems: 2, 2029 MaxItems: 3, 2030 }, 2031 }, 2032 }, 2033 }, 2034 }) 2035 2036 m := testModuleInline(t, map[string]string{ 2037 "main.tf": ` 2038 resource "test_instance" "a" { 2039 // MinItems 2 2040 foo { 2041 bar = "a" 2042 } 2043 foo { 2044 bar = "b" 2045 } 2046 } 2047 2048 resource "test_instance" "b" { 2049 // one dymamic block can satisfy MinItems of 2 2050 dynamic "foo" { 2051 for_each = test_instance.a.things 2052 content { 2053 bar = foo.value 2054 } 2055 } 2056 } 2057 2058 resource "test_instance" "c" { 2059 // we may have more than MaxItems dynamic blocks when they are unknown 2060 foo { 2061 bar = "b" 2062 } 2063 dynamic "foo" { 2064 for_each = test_instance.a.things 2065 content { 2066 bar = foo.value 2067 } 2068 } 2069 dynamic "foo" { 2070 for_each = test_instance.a.things 2071 content { 2072 bar = "${foo.value}-2" 2073 } 2074 } 2075 dynamic "foo" { 2076 for_each = test_instance.b.things 2077 content { 2078 bar = foo.value 2079 } 2080 } 2081 } 2082 `}) 2083 2084 ctx := testContext2(t, &ContextOpts{ 2085 Config: m, 2086 Providers: map[addrs.Provider]providers.Factory{ 2087 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 2088 }, 2089 }) 2090 2091 diags := ctx.Validate() 2092 if diags.HasErrors() { 2093 t.Fatal(diags.ErrWithWarnings()) 2094 } 2095 } 2096 2097 func TestContext2Validate_passInheritedProvider(t *testing.T) { 2098 m := testModuleInline(t, map[string]string{ 2099 "main.tf": ` 2100 terraform { 2101 required_providers { 2102 test = { 2103 source = "hashicorp/test" 2104 } 2105 } 2106 } 2107 2108 module "first" { 2109 source = "./first" 2110 providers = { 2111 test = test 2112 } 2113 } 2114 `, 2115 2116 // This module does not define a config for the test provider, but we 2117 // should be able to pass whatever the implied config is to a child 2118 // module. 2119 "first/main.tf": ` 2120 terraform { 2121 required_providers { 2122 test = { 2123 source = "hashicorp/test" 2124 } 2125 } 2126 } 2127 2128 module "second" { 2129 source = "./second" 2130 providers = { 2131 test.alias = test 2132 } 2133 }`, 2134 2135 "first/second/main.tf": ` 2136 terraform { 2137 required_providers { 2138 test = { 2139 source = "hashicorp/test" 2140 configuration_aliases = [test.alias] 2141 } 2142 } 2143 } 2144 2145 resource "test_object" "t" { 2146 provider = test.alias 2147 } 2148 `, 2149 }) 2150 2151 p := simpleMockProvider() 2152 ctx := testContext2(t, &ContextOpts{ 2153 Config: m, 2154 Providers: map[addrs.Provider]providers.Factory{ 2155 addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), 2156 }, 2157 }) 2158 2159 diags := ctx.Validate() 2160 if diags.HasErrors() { 2161 t.Fatal(diags.ErrWithWarnings()) 2162 } 2163 }