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