Merge pull request #24896 from bendrucker/validate-ignore-empty-provider
validate: ignore providers with no configuration
This commit is contained in:
commit
c9f372a62b
|
@ -135,6 +135,52 @@ func TestLocal_refreshValidate(t *testing.T) {
|
||||||
}
|
}
|
||||||
<-run.Done()
|
<-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 {
|
if !p.PrepareProviderConfigCalled {
|
||||||
t.Fatal("Prepare provider config should be called")
|
t.Fatal("Prepare provider config should be called")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
ami = "bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "test" {
|
||||||
|
value = "foo"
|
||||||
|
}
|
|
@ -595,8 +595,8 @@ func TestContext2Validate_providerConfig_bad(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContext2Validate_providerConfig_badEmpty(t *testing.T) {
|
func TestContext2Validate_providerConfig_skippedEmpty(t *testing.T) {
|
||||||
m := testModule(t, "validate-bad-pc-empty")
|
m := testModule(t, "validate-skipped-pc-empty")
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
p.GetSchemaReturn = &ProviderSchema{
|
p.GetSchemaReturn = &ProviderSchema{
|
||||||
Provider: &configschema.Block{
|
Provider: &configschema.Block{
|
||||||
|
@ -619,12 +619,12 @@ func TestContext2Validate_providerConfig_badEmpty(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
p.PrepareProviderConfigResponse = providers.PrepareProviderConfigResponse{
|
p.PrepareProviderConfigResponse = providers.PrepareProviderConfigResponse{
|
||||||
Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")),
|
Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("should not be called")),
|
||||||
}
|
}
|
||||||
|
|
||||||
diags := c.Validate()
|
diags := c.Validate()
|
||||||
if !diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
t.Fatalf("succeeded; want error")
|
t.Fatalf("unexpected error: %s", diags.Err())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,14 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider provi
|
||||||
|
|
||||||
configBody := buildProviderConfig(ctx, n.Addr, n.ProviderConfig())
|
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()
|
resp := provider.GetSchema()
|
||||||
diags = diags.Append(resp.Diagnostics)
|
diags = diags.Append(resp.Diagnostics)
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
|
@ -64,19 +72,7 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider provi
|
||||||
|
|
||||||
configVal, _, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
|
configVal, _, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
|
||||||
if evalDiags.HasErrors() {
|
if evalDiags.HasErrors() {
|
||||||
if n.Config == nil {
|
return diags.Append(evalDiags)
|
||||||
// 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)
|
diags = diags.Append(evalDiags)
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
func TestNodeApplyableProvider_Validate(t *testing.T) {
|
||||||
provider := &MockProvider{
|
provider := &MockProvider{
|
||||||
GetSchemaReturn: &ProviderSchema{
|
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{
|
node := NodeApplyableProvider{
|
||||||
NodeAbstractProvider: &NodeAbstractProvider{
|
NodeAbstractProvider: &NodeAbstractProvider{
|
||||||
Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
|
Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
|
||||||
|
@ -241,8 +296,8 @@ func TestNodeApplyableProvider_Validate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
diags := node.ValidateProvider(ctx, provider)
|
diags := node.ValidateProvider(ctx, provider)
|
||||||
if !diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
t.Error("missing expected error with invalid config")
|
t.Errorf("unexpected error with empty config: %s", diags.Err())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue