stacks: add support for external providers to validate context (#34701)

pull/34705/head
Liam Cervante 2 years ago committed by GitHub
parent 2472f7c773
commit c46515c5da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -131,7 +131,7 @@ func (b *Local) localRun(op *backend.Operation) (*backend.LocalRun, *configload.
// If validation is enabled, validate
if b.OpValidation {
log.Printf("[TRACE] backend/local: running validation operation")
validateDiags := ret.Core.Validate(ret.Config)
validateDiags := ret.Core.Validate(ret.Config, nil)
diags = diags.Append(validateDiags)
}
}

@ -583,7 +583,7 @@ func (runner *TestFileRunner) validate(config *configs.Config, run *moduletest.R
defer done()
log.Printf("[DEBUG] TestFileRunner: starting validate for %s/%s", file.Name, run.Name)
validateDiags = tfCtx.Validate(config)
validateDiags = tfCtx.Validate(config, nil)
log.Printf("[DEBUG] TestFileRunner: completed validate for %s/%s", file.Name, run.Name)
}()
waitDiags, cancelled := runner.wait(tfCtx, runningCtx, run, file, nil, moduletest.Running, start)

@ -94,7 +94,7 @@ func (c *ValidateCommand) validate(dir, testDir string, noTests bool) tfdiags.Di
return diags
}
return diags.Append(tfCtx.Validate(cfg))
return diags.Append(tfCtx.Validate(cfg, nil))
}
diags = diags.Append(validate(cfg))

@ -7178,7 +7178,7 @@ func TestContext2Apply_targetedDestroy(t *testing.T) {
},
})
if diags := ctx.Validate(m); diags.HasErrors() {
if diags := ctx.Validate(m, nil); diags.HasErrors() {
t.Fatalf("validate errors: %s", diags.Err())
}
@ -7736,7 +7736,7 @@ func TestContext2Apply_vars(t *testing.T) {
ctx := testContext2(t, opts)
m := fixture.Config
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if len(diags) != 0 {
t.Fatalf("bad: %s", diags.ErrWithWarnings())
}
@ -7799,7 +7799,7 @@ func TestContext2Apply_varsEnv(t *testing.T) {
ctx := testContext2(t, opts)
m := fixture.Config
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if len(diags) != 0 {
t.Fatalf("bad: %s", diags.ErrWithWarnings())
}
@ -9367,7 +9367,7 @@ func TestContext2Apply_invalidIndexRef(t *testing.T) {
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected validation failure: %s", diags.Err())
}

@ -141,7 +141,7 @@ output "second" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("expected error")
}
@ -311,7 +311,7 @@ func TestContext2Validate_providerFunctionDiagnostics(t *testing.T) {
},
})
diags := ctx.Validate(test.cfg)
diags := ctx.Validate(test.cfg, nil)
if !diags.HasErrors() {
t.Fatal("expected diagnsotics, got none")
}

@ -1864,7 +1864,7 @@ func TestContext2Plan_computedInFunction(t *testing.T) {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
assertNoErrors(t, diags)
_, diags = ctx.Plan(m, states.NewState(), DefaultPlanOpts)
@ -5264,7 +5264,7 @@ func TestContext2Plan_resourceNestedCount(t *testing.T) {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("validate errors: %s", diags.Err())
}
@ -5348,7 +5348,7 @@ func TestContext2Plan_selfRef(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected validation failure: %s", diags.Err())
}
@ -5384,7 +5384,7 @@ func TestContext2Plan_selfRefMulti(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected validation failure: %s", diags.Err())
}
@ -5420,7 +5420,7 @@ func TestContext2Plan_selfRefMultiAll(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected validation failure: %s", diags.Err())
}

@ -1229,7 +1229,7 @@ func TestContext2Validate(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if len(diags) != 0 {
t.Fatalf("unexpected error: %#v", diags.ErrWithWarnings())
}

@ -106,7 +106,7 @@ func TestNewContextRequiredVersion(t *testing.T) {
t.Fatalf("unexpected NewContext errors: %s", diags.Err())
}
diags = c.Validate(mod)
diags = c.Validate(mod, nil)
if diags.HasErrors() != tc.Err {
t.Fatalf("err: %s", diags.Err())
}
@ -165,7 +165,7 @@ terraform {}
t.Fatalf("unexpected NewContext errors: %s", diags.Err())
}
diags = c.Validate(mod)
diags = c.Validate(mod, nil)
if diags.HasErrors() != tc.Err {
t.Fatalf("err: %s", diags.Err())
}
@ -210,7 +210,7 @@ resource "implicit_thing" "b" {
// require doing some pretty weird things that aren't common enough to
// be worth the complexity to check for them.
validateDiags := ctx.Validate(cfg)
validateDiags := ctx.Validate(cfg, nil)
_, planDiags := ctx.Plan(cfg, nil, DefaultPlanOpts)
tests := map[string]tfdiags.Diagnostics{

@ -6,14 +6,35 @@ package terraform
import (
"log"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
"github.com/zclconf/go-cty/cty"
)
// ValidateOpts are the various options the affect the details of how Terraform
// will validate a configuration.
type ValidateOpts struct {
// ExternalProviders are clients for pre-configured providers that are
// treated as being passed into the root module from the caller. This
// is equivalent to writing a "providers" argument inside a "module"
// block in the Terraform language, but for the root module the caller
// is written in Go rather than the Terraform language.
//
// Note, that while Terraform Core will not call ValidateProviderConfig or
// ConfigureProvider on any providers in this map, as with the other context
// functions, the Validate function never calls ConfigureProvider anyway.
//
// Normally, the validate function would call the ValidateProviderConfig
// function on the provider, but the config may rely on variables that are
// not available to this function. Therefore, it is the responsibility of
// the caller to ensure that the provider configurations are valid.
ExternalProviders map[addrs.RootProviderConfig]providers.Interface
}
// Validate performs semantic validation of a configuration, and returns
// any warnings or errors.
//
@ -25,11 +46,18 @@ import (
// such as root module input variables. However, the Plan function includes
// all of the same checks as Validate, in addition to the other work it does
// to consider the previous run state and the planning options.
func (c *Context) Validate(config *configs.Config) tfdiags.Diagnostics {
//
// The opts can be nil, and the ExternalProviders field of the opts can be nil.
func (c *Context) Validate(config *configs.Config, opts *ValidateOpts) tfdiags.Diagnostics {
defer c.acquireRun("validate")()
var diags tfdiags.Diagnostics
if opts == nil {
// Just make sure we don't get any nil pointer exceptions later.
opts = &ValidateOpts{}
}
moreDiags := c.checkConfigDependencies(config)
diags = diags.Append(moreDiags)
// If required dependencies are not available then we'll bail early since
@ -60,11 +88,12 @@ func (c *Context) Validate(config *configs.Config) tfdiags.Diagnostics {
}
graph, moreDiags := (&PlanGraphBuilder{
Config: config,
Plugins: c.plugins,
State: states.NewState(),
RootVariableValues: varValues,
Operation: walkValidate,
Config: config,
Plugins: c.plugins,
State: states.NewState(),
RootVariableValues: varValues,
Operation: walkValidate,
ExternalProviderConfigs: opts.ExternalProviders,
}).Build(addrs.RootModuleInstance)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
@ -72,8 +101,9 @@ func (c *Context) Validate(config *configs.Config) tfdiags.Diagnostics {
}
walker, walkDiags := c.walk(graph, walkValidate, &graphWalkOpts{
Config: config,
ProviderFuncResults: providers.NewFunctionResultsTable(nil),
Config: config,
ProviderFuncResults: providers.NewFunctionResultsTable(nil),
ExternalProviderConfigs: opts.ExternalProviders,
})
diags = diags.Append(walker.NonFatalDiagnostics)
diags = diags.Append(walkDiags)

@ -9,6 +9,7 @@ import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/addrs"
@ -37,7 +38,7 @@ func TestContext2Validate_badCount(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -60,7 +61,7 @@ func TestContext2Validate_badResource_reference(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -86,7 +87,7 @@ func TestContext2Validate_badVar(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -163,7 +164,7 @@ func TestContext2Validate_computedVar(t *testing.T) {
return
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -203,7 +204,7 @@ func TestContext2Validate_computedInFunction(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -241,7 +242,7 @@ func TestContext2Validate_countComputed(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -265,7 +266,7 @@ func TestContext2Validate_countNegative(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -291,7 +292,7 @@ func TestContext2Validate_countVariable(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -345,7 +346,7 @@ func TestContext2Validate_moduleBadOutput(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -371,7 +372,7 @@ func TestContext2Validate_moduleGood(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -400,7 +401,7 @@ func TestContext2Validate_moduleBadResource(t *testing.T) {
Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")),
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -427,7 +428,7 @@ func TestContext2Validate_moduleDepsShouldNotCycle(t *testing.T) {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -468,7 +469,7 @@ func TestContext2Validate_moduleProviderVar(t *testing.T) {
return
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -509,7 +510,7 @@ func TestContext2Validate_moduleProviderInheritUnused(t *testing.T) {
return
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -548,7 +549,7 @@ func TestContext2Validate_orphans(t *testing.T) {
}
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -584,7 +585,7 @@ func TestContext2Validate_providerConfig_bad(t *testing.T) {
Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")),
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if len(diags) != 1 {
t.Fatalf("wrong number of diagnostics %d; want %d", len(diags), 1)
}
@ -623,7 +624,7 @@ func TestContext2Validate_providerConfig_skippedEmpty(t *testing.T) {
Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("should not be called")),
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -655,7 +656,7 @@ func TestContext2Validate_providerConfig_good(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -690,7 +691,7 @@ func TestContext2Validate_requiredProviderConfig(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -726,7 +727,7 @@ func TestContext2Validate_provisionerConfig_bad(t *testing.T) {
Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")),
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -758,7 +759,7 @@ func TestContext2Validate_badResourceConnection(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
t.Log(diags.Err())
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
@ -791,7 +792,7 @@ func TestContext2Validate_badProvisionerConnection(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
t.Log(diags.Err())
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
@ -840,7 +841,7 @@ func TestContext2Validate_provisionerConfig_good(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -906,7 +907,7 @@ func TestContext2Validate_resourceConfig_bad(t *testing.T) {
Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")),
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -932,7 +933,7 @@ func TestContext2Validate_resourceConfig_good(t *testing.T) {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -970,7 +971,7 @@ func TestContext2Validate_tainted(t *testing.T) {
}
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -1007,7 +1008,7 @@ func TestContext2Validate_targetedDestroy(t *testing.T) {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -1039,7 +1040,7 @@ func TestContext2Validate_varRefUnknown(t *testing.T) {
return providers.ValidateResourceConfigResponse{}
}
c.Validate(m)
c.Validate(m, nil)
// Input variables are always unknown during the validate walk, because
// we're checking for validity of all possible input values. Validity
@ -1075,7 +1076,7 @@ func TestContext2Validate_interpolateVar(t *testing.T) {
UIInput: input,
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -1107,7 +1108,7 @@ func TestContext2Validate_interpolateComputedModuleVarDef(t *testing.T) {
UIInput: input,
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -1127,7 +1128,7 @@ func TestContext2Validate_interpolateMap(t *testing.T) {
UIInput: input,
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -1177,7 +1178,7 @@ resource "aws_instance" "foo" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.Err())
}
@ -1208,7 +1209,7 @@ output "out" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1244,7 +1245,7 @@ resource "aws_instance" "foo" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1279,7 +1280,7 @@ output "root" {
ctx := testContext2(t, &ContextOpts{})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.Err())
}
@ -1302,7 +1303,7 @@ output "out" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1332,7 +1333,7 @@ output "out" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1362,7 +1363,7 @@ output "out" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1391,7 +1392,7 @@ resource "test_instance" "bar" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1423,7 +1424,7 @@ resource "test_instance" "bar" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1447,7 +1448,7 @@ func TestContext2Validate_variableCustomValidationsFail(t *testing.T) {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1481,7 +1482,7 @@ variable "test" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error\ngot: %s", diags.Err().Error())
}
@ -1542,7 +1543,7 @@ resource "aws_instance" "foo" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -1569,7 +1570,7 @@ resource "aws_instance" "foo" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1599,7 +1600,7 @@ resource "aws_instance" "foo" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1680,7 +1681,7 @@ output "out" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -1707,7 +1708,7 @@ output "out" {
`,
})
diags := testContext2(t, &ContextOpts{}).Validate(m)
diags := testContext2(t, &ContextOpts{}).Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1745,7 +1746,7 @@ output "out" {
`,
})
diags := testContext2(t, &ContextOpts{}).Validate(m)
diags := testContext2(t, &ContextOpts{}).Validate(m, nil)
if !diags.HasErrors() {
t.Fatal("succeeded; want errors")
}
@ -1793,7 +1794,7 @@ resource "test_instance" "a" {
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.Err())
}
@ -1843,7 +1844,7 @@ func TestContext2Validate_sensitiveProvisionerConfig(t *testing.T) {
return pr.ValidateProvisionerConfigResponse
}
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
@ -1937,7 +1938,7 @@ resource "test_instance" "c" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2004,7 +2005,7 @@ resource "test_object" "t" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2061,7 +2062,7 @@ output "out" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2094,7 +2095,7 @@ func TestContext2Validate_nonNullableVariableDefaultValidation(t *testing.T) {
ctx := testContext2(t, &ContextOpts{})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2137,7 +2138,7 @@ resource "aws_instance" "test" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2180,7 +2181,7 @@ resource "aws_instance" "test" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -2226,7 +2227,7 @@ resource "aws_instance" "test" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -2267,7 +2268,7 @@ resource "aws_instance" "test" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2316,7 +2317,7 @@ resource "aws_instance" "test" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -2357,7 +2358,7 @@ resource "aws_instance" "test" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
@ -2403,7 +2404,7 @@ resource "aws_instance" "test" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2446,7 +2447,7 @@ resource "aws_instance" "test" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2480,7 +2481,7 @@ locals {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
warn := diags.ErrWithWarnings().Error()
if !strings.Contains(warn, `The attribute "foo" is deprecated`) {
t.Fatalf("expected deprecated warning, got: %q\n", warn)
@ -2511,7 +2512,7 @@ resource "aws_instance" "follow" {
},
})
diags := c.Validate(m)
diags := c.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2593,7 +2594,7 @@ output "result" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
@ -2625,7 +2626,7 @@ output "result" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if p.CallFunctionCalled {
t.Error("CallFunction was called, but should not have been")
}
@ -2660,7 +2661,7 @@ output "result" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if p.CallFunctionCalled {
t.Error("CallFunction was called, but should not have been")
}
@ -2694,7 +2695,7 @@ output "result" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if p.CallFunctionCalled {
t.Error("CallFunction was called, but should not have been")
}
@ -2728,7 +2729,7 @@ output "result" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if p.CallFunctionCalled {
t.Error("CallFunction was called, but should not have been")
}
@ -2762,7 +2763,7 @@ output "result" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if p.CallFunctionCalled {
t.Error("CallFunction was called, but should not have been")
}
@ -2796,7 +2797,7 @@ output "result" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if p.CallFunctionCalled {
t.Error("CallFunction was called, but should not have been")
}
@ -2833,7 +2834,7 @@ output "result" {
// For this case, validation should succeed without calling the
// function yet, because the function doesn't declare that it handles
// unknown values and so we must defer validation until a later phase.
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if p.CallFunctionCalled {
t.Error("CallFunction was called, but should not have been")
}
@ -2862,7 +2863,7 @@ output "result" {
},
})
diags := ctx.Validate(m)
diags := ctx.Validate(m, nil)
if p.CallFunctionCalled {
t.Error("CallFunction was called, but should not have been")
}
@ -2876,3 +2877,107 @@ output "result" {
}
})
}
func TestContextValidate_externalProviders(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
terraform {
required_providers {
bar = {
source = "hashicorp/bar"
}
}
}
provider "bar" {}
resource "bar_instance" "test" {
foo = "foo" # should be an int
}
`,
})
mustNotConfigure := func(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse {
return providers.ConfigureProviderResponse{
Diagnostics: tfdiags.Diagnostics{
tfdiags.Sourceless(
tfdiags.Error,
"Pre-configured provider was reconfigured by the modules runtime",
"An externally-configured provider should not have its ConfigureProvider function called during planning.",
),
},
}
}
providerAddr := addrs.NewDefaultProvider("bar")
providerConfigAddr := addrs.RootProviderConfig{
Provider: providerAddr,
}
provider := &testing_provider.MockProvider{
GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{
Provider: providers.Schema{
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
// We have a required attribute that is not set, we're
// expecting this to not matter as we shouldn't validate
// the provider configuration as we're using an external
// provider.
"required": {
Type: cty.String,
Required: true,
},
},
},
},
ResourceTypes: map[string]providers.Schema{
"bar_instance": {
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
// We should still validate this attribute as being
// incorrect, even though we have an external
// provider.
"foo": {
Type: cty.Number,
Required: true,
},
},
},
},
},
},
ConfigureProviderFn: mustNotConfigure,
}
ctx, diags := NewContext(&ContextOpts{
PreloadedProviderSchemas: map[addrs.Provider]providers.ProviderSchema{
providerAddr: *provider.GetProviderSchemaResponse,
},
})
assertNoDiagnostics(t, diags)
// Many of the MockProvider methods check for this, so we'll set it to be
// true externally.
provider.ConfigureProviderCalled = true
diags = ctx.Validate(m, &ValidateOpts{
ExternalProviders: map[addrs.RootProviderConfig]providers.Interface{
providerConfigAddr: provider,
},
})
// We should have exactly one diagnostic, stating there was an error in the
// resource. But nothing complaining about the provider itself.
if len(diags) != 1 {
t.Fatalf("expected exactly one diagnostic, got %d", len(diags))
}
if diff := cmp.Diff(diags[0].Description().Summary, "Incorrect attribute value type"); len(diff) > 0 {
t.Errorf("unexpected diagnostic summary: %s", diff)
}
if diff := cmp.Diff(diags[0].Description().Detail, "Inappropriate value for attribute \"foo\": a number is required."); len(diff) > 0 {
t.Errorf("unexpected diagnostic detail: %s", diff)
}
}

Loading…
Cancel
Save