Merge pull request #24896 from bendrucker/validate-ignore-empty-provider

validate: ignore providers with no configuration
This commit is contained in:
Pam Selle 2021-01-07 15:30:57 -05:00 committed by GitHub
commit c9f372a62b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 125 additions and 21 deletions

View File

@ -135,6 +135,52 @@ func TestLocal_refreshValidate(t *testing.T) {
}
<-run.Done()
checkState(t, b.StateOutPath, `
test_instance.foo:
ID = yes
provider = provider["registry.terraform.io/hashicorp/test"]
`)
}
func TestLocal_refreshValidateProviderConfigured(t *testing.T) {
b, cleanup := TestLocal(t)
defer cleanup()
schema := &terraform.ProviderSchema{
Provider: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"value": {Type: cty.String, Optional: true},
},
},
ResourceTypes: map[string]*configschema.Block{
"test_instance": {
Attributes: map[string]*configschema.Attribute{
"id": {Type: cty.String, Computed: true},
"ami": {Type: cty.String, Optional: true},
},
},
},
}
p := TestLocalProvider(t, b, "test", schema)
testStateFile(t, b.StatePath, testRefreshState())
p.ReadResourceFn = nil
p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("yes"),
})}
// Enable validation
b.OpValidation = true
op, configCleanup := testOperationRefresh(t, "./testdata/refresh-provider-config")
defer configCleanup()
run, err := b.Operation(context.Background(), op)
if err != nil {
t.Fatalf("bad: %s", err)
}
<-run.Done()
if !p.PrepareProviderConfigCalled {
t.Fatal("Prepare provider config should be called")
}

View File

@ -0,0 +1,7 @@
resource "test_instance" "foo" {
ami = "bar"
}
provider "test" {
value = "foo"
}

View File

@ -595,8 +595,8 @@ func TestContext2Validate_providerConfig_bad(t *testing.T) {
}
}
func TestContext2Validate_providerConfig_badEmpty(t *testing.T) {
m := testModule(t, "validate-bad-pc-empty")
func TestContext2Validate_providerConfig_skippedEmpty(t *testing.T) {
m := testModule(t, "validate-skipped-pc-empty")
p := testProvider("aws")
p.GetSchemaReturn = &ProviderSchema{
Provider: &configschema.Block{
@ -619,12 +619,12 @@ func TestContext2Validate_providerConfig_badEmpty(t *testing.T) {
})
p.PrepareProviderConfigResponse = providers.PrepareProviderConfigResponse{
Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")),
Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("should not be called")),
}
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
}

View File

@ -48,6 +48,14 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider provi
configBody := buildProviderConfig(ctx, n.Addr, n.ProviderConfig())
// if a provider config is empty (only an alias), return early and don't continue
// validation. validate doesn't need to fully configure the provider itself, so
// skipping a provider with an implied configuration won't prevent other validation from completing.
_, noConfigDiags := configBody.Content(&hcl.BodySchema{})
if !noConfigDiags.HasErrors() {
return nil
}
resp := provider.GetSchema()
diags = diags.Append(resp.Diagnostics)
if diags.HasErrors() {
@ -64,20 +72,8 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider provi
configVal, _, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
if evalDiags.HasErrors() {
if n.Config == nil {
// If there isn't an explicit "provider" block in the configuration,
// this error message won't be very clear. Add some detail to the
// error message in this case.
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid provider configuration",
fmt.Sprintf(providerConfigErr, evalDiags.Err(), n.Addr.Provider),
))
return diags
} else {
return diags.Append(evalDiags)
}
}
diags = diags.Append(evalDiags)
// If our config value contains any marked values, ensure those are

View File

@ -196,6 +196,40 @@ func TestNodeApplyableProviderExecute_sensitiveValidate(t *testing.T) {
}
}
func TestNodeApplyableProviderExecute_emptyValidate(t *testing.T) {
config := &configs.Provider{
Name: "foo",
Config: configs.SynthBody("", map[string]cty.Value{}),
}
provider := mockProviderWithConfigSchema(&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"test_string": {
Type: cty.String,
Required: true,
},
},
})
providerAddr := addrs.AbsProviderConfig{
Module: addrs.RootModule,
Provider: addrs.NewDefaultProvider("foo"),
}
n := &NodeApplyableProvider{&NodeAbstractProvider{
Addr: providerAddr,
Config: config,
}}
ctx := &MockEvalContext{ProviderProvider: provider}
ctx.installSimpleEval()
if err := n.Execute(ctx, walkValidate); err != nil {
t.Fatalf("err: %s", err)
}
if ctx.ConfigureProviderCalled {
t.Fatal("should not be called")
}
}
func TestNodeApplyableProvider_Validate(t *testing.T) {
provider := &MockProvider{
GetSchemaReturn: &ProviderSchema{
@ -233,7 +267,28 @@ func TestNodeApplyableProvider_Validate(t *testing.T) {
}
})
t.Run("missing required config", func(t *testing.T) {
t.Run("invalid", func(t *testing.T) {
config := &configs.Provider{
Name: "test",
Config: configs.SynthBody("", map[string]cty.Value{
"region": cty.MapValEmpty(cty.String),
}),
}
node := NodeApplyableProvider{
NodeAbstractProvider: &NodeAbstractProvider{
Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
Config: config,
},
}
diags := node.ValidateProvider(ctx, provider)
if !diags.HasErrors() {
t.Error("missing expected error with invalid config")
}
})
t.Run("empty config", func(t *testing.T) {
node := NodeApplyableProvider{
NodeAbstractProvider: &NodeAbstractProvider{
Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
@ -241,8 +296,8 @@ func TestNodeApplyableProvider_Validate(t *testing.T) {
}
diags := node.ValidateProvider(ctx, provider)
if !diags.HasErrors() {
t.Error("missing expected error with invalid config")
if diags.HasErrors() {
t.Errorf("unexpected error with empty config: %s", diags.Err())
}
})
}