update providers.Schemas type

Use the global providers.SchemaCache and update all schema access to the
providers.Schemas, except where the provider.GetProviderSchemaResponse
type name would be expected.

Some tests that reuse provider factories needed a little more careful
handling. Change the fixed func to only reset the provider on the first
call.
This commit is contained in:
James Bardin 2023-07-05 17:31:39 -04:00
parent 9d9746560f
commit ec3a38e5ed
31 changed files with 390 additions and 400 deletions

View File

@ -146,7 +146,7 @@ func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) {
} }
func (c *Context) Schemas(config *configs.Config, state *states.State) (*Schemas, tfdiags.Diagnostics) { func (c *Context) Schemas(config *configs.Config, state *states.State) (*Schemas, tfdiags.Diagnostics) {
// TODO: This method gets called multiple times on the same context with // FIXME: This method gets called multiple times on the same context with
// the same inputs by different parts of Terraform that all need the // the same inputs by different parts of Terraform that all need the
// schemas, and it's typically quite expensive because it has to spin up // schemas, and it's typically quite expensive because it has to spin up
// plugins to gather their schemas, so it'd be good to have some caching // plugins to gather their schemas, so it'd be good to have some caching

View File

@ -2782,13 +2782,13 @@ resource "test_resource" "a" {
}, },
}) })
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
t.Run("conditions pass", func(t *testing.T) { t.Run("conditions pass", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
m := req.ProposedNewState.AsValueMap() m := req.ProposedNewState.AsValueMap()
m["output"] = cty.StringVal("bar") m["output"] = cty.StringVal("bar")
@ -2820,6 +2820,12 @@ resource "test_resource" "a" {
}) })
t.Run("precondition fail", func(t *testing.T) { t.Run("precondition fail", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
_, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ _, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
Mode: plans.NormalMode, Mode: plans.NormalMode,
SetVariables: InputValues{ SetVariables: InputValues{
@ -2841,6 +2847,12 @@ resource "test_resource" "a" {
}) })
t.Run("precondition fail refresh-only", func(t *testing.T) { t.Run("precondition fail refresh-only", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
state := states.BuildState(func(s *states.SyncState) { state := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(mustResourceInstanceAddr("test_resource.a"), &states.ResourceInstanceObjectSrc{ s.SetResourceInstanceCurrent(mustResourceInstanceAddr("test_resource.a"), &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"value":"boop","output":"blorp"}`), AttrsJSON: []byte(`{"value":"boop","output":"blorp"}`),
@ -2869,6 +2881,12 @@ resource "test_resource" "a" {
}) })
t.Run("postcondition fail", func(t *testing.T) { t.Run("postcondition fail", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
m := req.ProposedNewState.AsValueMap() m := req.ProposedNewState.AsValueMap()
m["output"] = cty.StringVal("") m["output"] = cty.StringVal("")
@ -2898,6 +2916,12 @@ resource "test_resource" "a" {
}) })
t.Run("postcondition fail refresh-only", func(t *testing.T) { t.Run("postcondition fail refresh-only", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
state := states.BuildState(func(s *states.SyncState) { state := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(mustResourceInstanceAddr("test_resource.a"), &states.ResourceInstanceObjectSrc{ s.SetResourceInstanceCurrent(mustResourceInstanceAddr("test_resource.a"), &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"value":"boop","output":"blorp"}`), AttrsJSON: []byte(`{"value":"boop","output":"blorp"}`),
@ -2945,6 +2969,12 @@ resource "test_resource" "a" {
}) })
t.Run("precondition and postcondition fail refresh-only", func(t *testing.T) { t.Run("precondition and postcondition fail refresh-only", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
state := states.BuildState(func(s *states.SyncState) { state := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(mustResourceInstanceAddr("test_resource.a"), &states.ResourceInstanceObjectSrc{ s.SetResourceInstanceCurrent(mustResourceInstanceAddr("test_resource.a"), &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"value":"boop","output":"blorp"}`), AttrsJSON: []byte(`{"value":"boop","output":"blorp"}`),
@ -3054,13 +3084,12 @@ resource "test_resource" "a" {
}, },
}) })
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
t.Run("conditions pass", func(t *testing.T) { t.Run("conditions pass", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{ State: cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("boop"), "foo": cty.StringVal("boop"),
@ -3106,6 +3135,11 @@ resource "test_resource" "a" {
}) })
t.Run("precondition fail", func(t *testing.T) { t.Run("precondition fail", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
_, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ _, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
Mode: plans.NormalMode, Mode: plans.NormalMode,
SetVariables: InputValues{ SetVariables: InputValues{
@ -3127,6 +3161,11 @@ resource "test_resource" "a" {
}) })
t.Run("precondition fail refresh-only", func(t *testing.T) { t.Run("precondition fail refresh-only", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
Mode: plans.RefreshOnlyMode, Mode: plans.RefreshOnlyMode,
SetVariables: InputValues{ SetVariables: InputValues{
@ -3160,6 +3199,11 @@ resource "test_resource" "a" {
}) })
t.Run("postcondition fail", func(t *testing.T) { t.Run("postcondition fail", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{ State: cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("boop"), "foo": cty.StringVal("boop"),
@ -3187,6 +3231,11 @@ resource "test_resource" "a" {
}) })
t.Run("postcondition fail refresh-only", func(t *testing.T) { t.Run("postcondition fail refresh-only", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{ State: cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("boop"), "foo": cty.StringVal("boop"),
@ -3223,6 +3272,11 @@ resource "test_resource" "a" {
}) })
t.Run("precondition and postcondition fail refresh-only", func(t *testing.T) { t.Run("precondition and postcondition fail refresh-only", func(t *testing.T) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{ State: cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("nope"), "foo": cty.StringVal("nope"),

View File

@ -6,7 +6,6 @@ package terraform
import ( import (
"fmt" "fmt"
"log" "log"
"sync"
"github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/configs/configschema"
@ -21,15 +20,6 @@ import (
type contextPlugins struct { type contextPlugins struct {
providerFactories map[addrs.Provider]providers.Factory providerFactories map[addrs.Provider]providers.Factory
provisionerFactories map[string]provisioners.Factory provisionerFactories map[string]provisioners.Factory
// We memoize the schemas we've previously loaded in here, to avoid
// repeatedly paying the cost of activating the same plugins to access
// their schemas in various different spots. We use schemas for many
// purposes in Terraform, so there isn't a single choke point where
// it makes sense to preload all of them.
providerSchemas map[addrs.Provider]*ProviderSchema
provisionerSchemas map[string]*configschema.Block
schemasLock sync.Mutex
} }
func newContextPlugins(providerFactories map[addrs.Provider]providers.Factory, provisionerFactories map[string]provisioners.Factory) *contextPlugins { func newContextPlugins(providerFactories map[addrs.Provider]providers.Factory, provisionerFactories map[string]provisioners.Factory) *contextPlugins {
@ -37,15 +27,9 @@ func newContextPlugins(providerFactories map[addrs.Provider]providers.Factory, p
providerFactories: providerFactories, providerFactories: providerFactories,
provisionerFactories: provisionerFactories, provisionerFactories: provisionerFactories,
} }
ret.init()
return ret return ret
} }
func (cp *contextPlugins) init() {
cp.providerSchemas = make(map[addrs.Provider]*ProviderSchema, len(cp.providerFactories))
cp.provisionerSchemas = make(map[string]*configschema.Block, len(cp.provisionerFactories))
}
func (cp *contextPlugins) HasProvider(addr addrs.Provider) bool { func (cp *contextPlugins) HasProvider(addr addrs.Provider) bool {
_, ok := cp.providerFactories[addr] _, ok := cp.providerFactories[addr]
return ok return ok
@ -81,70 +65,53 @@ func (cp *contextPlugins) NewProvisionerInstance(typ string) (provisioners.Inter
// ProviderSchema memoizes results by unique provider address, so it's fine // ProviderSchema memoizes results by unique provider address, so it's fine
// to repeatedly call this method with the same address if various different // to repeatedly call this method with the same address if various different
// parts of Terraform all need the same schema information. // parts of Terraform all need the same schema information.
func (cp *contextPlugins) ProviderSchema(addr addrs.Provider) (*ProviderSchema, error) { func (cp *contextPlugins) ProviderSchema(addr addrs.Provider) (providers.Schemas, error) {
cp.schemasLock.Lock()
defer cp.schemasLock.Unlock()
if schema, ok := cp.providerSchemas[addr]; ok {
return schema, nil
}
log.Printf("[TRACE] terraform.contextPlugins: Initializing provider %q to read its schema", addr) log.Printf("[TRACE] terraform.contextPlugins: Initializing provider %q to read its schema", addr)
// check the global schema cache first
schemas, ok := providers.SchemaCache.Get(addr)
if ok {
return schemas, nil
}
provider, err := cp.NewProviderInstance(addr) provider, err := cp.NewProviderInstance(addr)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to instantiate provider %q to obtain schema: %s", addr, err) return schemas, fmt.Errorf("failed to instantiate provider %q to obtain schema: %s", addr, err)
} }
defer provider.Close() defer provider.Close()
resp := provider.GetProviderSchema() resp := provider.GetProviderSchema()
if resp.Diagnostics.HasErrors() { if resp.Diagnostics.HasErrors() {
return nil, fmt.Errorf("failed to retrieve schema from provider %q: %s", addr, resp.Diagnostics.Err()) return resp, fmt.Errorf("failed to retrieve schema from provider %q: %s", addr, resp.Diagnostics.Err())
}
s := &ProviderSchema{
Provider: resp.Provider.Block,
ResourceTypes: make(map[string]*configschema.Block),
DataSources: make(map[string]*configschema.Block),
ResourceTypeSchemaVersions: make(map[string]uint64),
} }
if resp.Provider.Version < 0 { if resp.Provider.Version < 0 {
// We're not using the version numbers here yet, but we'll check // We're not using the version numbers here yet, but we'll check
// for validity anyway in case we start using them in future. // for validity anyway in case we start using them in future.
return nil, fmt.Errorf("provider %s has invalid negative schema version for its configuration blocks,which is a bug in the provider ", addr) return resp, fmt.Errorf("provider %s has invalid negative schema version for its configuration blocks,which is a bug in the provider ", addr)
} }
for t, r := range resp.ResourceTypes { for t, r := range resp.ResourceTypes {
if err := r.Block.InternalValidate(); err != nil { if err := r.Block.InternalValidate(); err != nil {
return nil, fmt.Errorf("provider %s has invalid schema for managed resource type %q, which is a bug in the provider: %q", addr, t, err) return resp, fmt.Errorf("provider %s has invalid schema for managed resource type %q, which is a bug in the provider: %q", addr, t, err)
} }
s.ResourceTypes[t] = r.Block
s.ResourceTypeSchemaVersions[t] = uint64(r.Version)
if r.Version < 0 { if r.Version < 0 {
return nil, fmt.Errorf("provider %s has invalid negative schema version for managed resource type %q, which is a bug in the provider", addr, t) return resp, fmt.Errorf("provider %s has invalid negative schema version for managed resource type %q, which is a bug in the provider", addr, t)
} }
} }
for t, d := range resp.DataSources { for t, d := range resp.DataSources {
if err := d.Block.InternalValidate(); err != nil { if err := d.Block.InternalValidate(); err != nil {
return nil, fmt.Errorf("provider %s has invalid schema for data resource type %q, which is a bug in the provider: %q", addr, t, err) return resp, fmt.Errorf("provider %s has invalid schema for data resource type %q, which is a bug in the provider: %q", addr, t, err)
} }
s.DataSources[t] = d.Block
if d.Version < 0 { if d.Version < 0 {
// We're not using the version numbers here yet, but we'll check // We're not using the version numbers here yet, but we'll check
// for validity anyway in case we start using them in future. // for validity anyway in case we start using them in future.
return nil, fmt.Errorf("provider %s has invalid negative schema version for data resource type %q, which is a bug in the provider", addr, t) return resp, fmt.Errorf("provider %s has invalid negative schema version for data resource type %q, which is a bug in the provider", addr, t)
} }
} }
if resp.ProviderMeta.Block != nil { return resp, nil
s.ProviderMeta = resp.ProviderMeta.Block
}
cp.providerSchemas[addr] = s
return s, nil
} }
// ProviderConfigSchema is a helper wrapper around ProviderSchema which first // ProviderConfigSchema is a helper wrapper around ProviderSchema which first
@ -157,7 +124,7 @@ func (cp *contextPlugins) ProviderConfigSchema(providerAddr addrs.Provider) (*co
return nil, err return nil, err
} }
return providerSchema.Provider, nil return providerSchema.Provider.Block, nil
} }
// ResourceTypeSchema is a helper wrapper around ProviderSchema which first // ResourceTypeSchema is a helper wrapper around ProviderSchema which first
@ -188,13 +155,6 @@ func (cp *contextPlugins) ResourceTypeSchema(providerAddr addrs.Provider, resour
// to repeatedly call this method with the same name if various different // to repeatedly call this method with the same name if various different
// parts of Terraform all need the same schema information. // parts of Terraform all need the same schema information.
func (cp *contextPlugins) ProvisionerSchema(typ string) (*configschema.Block, error) { func (cp *contextPlugins) ProvisionerSchema(typ string) (*configschema.Block, error) {
cp.schemasLock.Lock()
defer cp.schemasLock.Unlock()
if schema, ok := cp.provisionerSchemas[typ]; ok {
return schema, nil
}
log.Printf("[TRACE] terraform.contextPlugins: Initializing provisioner %q to read its schema", typ) log.Printf("[TRACE] terraform.contextPlugins: Initializing provisioner %q to read its schema", typ)
provisioner, err := cp.NewProvisionerInstance(typ) provisioner, err := cp.NewProvisionerInstance(typ)
if err != nil { if err != nil {
@ -207,6 +167,5 @@ func (cp *contextPlugins) ProvisionerSchema(typ string) (*configschema.Block, er
return nil, fmt.Errorf("failed to retrieve schema from provisioner %q: %s", typ, resp.Diagnostics.Err()) return nil, fmt.Errorf("failed to retrieve schema from provisioner %q: %s", typ, resp.Diagnostics.Err())
} }
cp.provisionerSchemas[typ] = resp.Provisioner
return resp.Provisioner, nil return resp.Provisioner, nil
} }

View File

@ -41,7 +41,6 @@ func simpleMockPluginLibrary() *contextPlugins {
}, },
}, },
} }
ret.init() // prepare the internal cache data structures
return ret return ret
} }

View File

@ -57,7 +57,7 @@ type EvalContext interface {
// //
// This method expects an _absolute_ provider configuration address, since // This method expects an _absolute_ provider configuration address, since
// resources in one module are able to use providers from other modules. // resources in one module are able to use providers from other modules.
ProviderSchema(addrs.AbsProviderConfig) (*ProviderSchema, error) ProviderSchema(addrs.AbsProviderConfig) (providers.Schemas, error)
// CloseProvider closes provider connections that aren't needed anymore. // CloseProvider closes provider connections that aren't needed anymore.
// //

View File

@ -149,7 +149,22 @@ func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.
return ctx.ProviderCache[addr.String()] return ctx.ProviderCache[addr.String()]
} }
func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (*ProviderSchema, error) { func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (providers.Schemas, error) {
// first see if we have already have an initialized provider to avoid
// re-loading it only for the schema
p := ctx.Provider(addr)
if p != nil {
resp := p.GetProviderSchema()
// convert any diagnostics here in case this is the first call
// FIXME: better control provider instantiation so we can be sure this
// won't be the first call to ProviderSchema
var err error
if resp.Diagnostics.HasErrors() {
err = resp.Diagnostics.ErrWithWarnings()
}
return resp, err
}
return ctx.Plugins.ProviderSchema(addr.Provider) return ctx.Plugins.ProviderSchema(addr.Provider)
} }
@ -181,16 +196,6 @@ func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, c
return diags return diags
} }
providerSchema, err := ctx.ProviderSchema(addr)
if err != nil {
diags = diags.Append(fmt.Errorf("failed to read schema for %s: %s", addr, err))
return diags
}
if providerSchema == nil {
diags = diags.Append(fmt.Errorf("schema for %s is not available", addr))
return diags
}
req := providers.ConfigureProviderRequest{ req := providers.ConfigureProviderRequest{
TerraformVersion: version.String(), TerraformVersion: version.String(),
Config: cfg, Config: cfg,

View File

@ -46,7 +46,7 @@ type MockEvalContext struct {
ProviderSchemaCalled bool ProviderSchemaCalled bool
ProviderSchemaAddr addrs.AbsProviderConfig ProviderSchemaAddr addrs.AbsProviderConfig
ProviderSchemaSchema *ProviderSchema ProviderSchemaSchema providers.Schemas
ProviderSchemaError error ProviderSchemaError error
CloseProviderCalled bool CloseProviderCalled bool
@ -190,7 +190,7 @@ func (c *MockEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Inter
return c.ProviderProvider return c.ProviderProvider
} }
func (c *MockEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (*ProviderSchema, error) { func (c *MockEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (providers.Schemas, error) {
c.ProviderSchemaCalled = true c.ProviderSchemaCalled = true
c.ProviderSchemaAddr = addr c.ProviderSchemaAddr = addr
return c.ProviderSchemaSchema, c.ProviderSchemaError return c.ProviderSchemaSchema, c.ProviderSchemaError

View File

@ -43,20 +43,20 @@ func buildProviderConfig(ctx EvalContext, addr addrs.AbsProviderConfig, config *
} }
// getProvider returns the providers.Interface and schema for a given provider. // getProvider returns the providers.Interface and schema for a given provider.
func getProvider(ctx EvalContext, addr addrs.AbsProviderConfig) (providers.Interface, *ProviderSchema, error) { func getProvider(ctx EvalContext, addr addrs.AbsProviderConfig) (providers.Interface, providers.Schemas, error) {
if addr.Provider.Type == "" { if addr.Provider.Type == "" {
// Should never happen // Should never happen
panic("GetProvider used with uninitialized provider configuration address") panic("GetProvider used with uninitialized provider configuration address")
} }
provider := ctx.Provider(addr) provider := ctx.Provider(addr)
if provider == nil { if provider == nil {
return nil, &ProviderSchema{}, fmt.Errorf("provider %s not initialized", addr) return nil, providers.Schemas{}, fmt.Errorf("provider %s not initialized", addr)
} }
// Not all callers require a schema, so we will leave checking for a nil // Not all callers require a schema, so we will leave checking for a nil
// schema to the callers. // schema to the callers.
schema, err := ctx.ProviderSchema(addr) schema, err := ctx.ProviderSchema(addr)
if err != nil { if err != nil {
return nil, &ProviderSchema{}, fmt.Errorf("failed to read schema for provider %s: %w", addr, err) return nil, providers.Schemas{}, fmt.Errorf("failed to read schema for provider %s: %w", addr, err)
} }
return provider, schema, nil return provider, schema, nil
} }

View File

@ -15,6 +15,7 @@ import (
"github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/lang/marks"
"github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags" "github.com/hashicorp/terraform/internal/tfdiags"
) )
@ -264,71 +265,72 @@ func TestEvaluatorGetResource(t *testing.T) {
}, },
}, },
State: stateSync, State: stateSync,
Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]*ProviderSchema{ Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]providers.Schemas{
addrs.NewDefaultProvider("test"): { addrs.NewDefaultProvider("test"): {
Provider: &configschema.Block{}, ResourceTypes: map[string]providers.Schema{
ResourceTypes: map[string]*configschema.Block{
"test_resource": { "test_resource": {
Attributes: map[string]*configschema.Attribute{ Block: &configschema.Block{
"id": { Attributes: map[string]*configschema.Attribute{
Type: cty.String, "id": {
Computed: true, Type: cty.String,
}, Computed: true,
"value": {
Type: cty.String,
Computed: true,
Sensitive: true,
},
},
BlockTypes: map[string]*configschema.NestedBlock{
"nesting_list": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"value": {Type: cty.String, Optional: true},
"sensitive_value": {Type: cty.String, Optional: true, Sensitive: true},
},
}, },
Nesting: configschema.NestingList, "value": {
}, Type: cty.String,
"nesting_map": { Computed: true,
Block: configschema.Block{ Sensitive: true,
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Sensitive: true},
},
}, },
Nesting: configschema.NestingMap,
}, },
"nesting_set": { BlockTypes: map[string]*configschema.NestedBlock{
Block: configschema.Block{ "nesting_list": {
Attributes: map[string]*configschema.Attribute{ Block: configschema.Block{
"baz": {Type: cty.String, Optional: true, Sensitive: true}, Attributes: map[string]*configschema.Attribute{
}, "value": {Type: cty.String, Optional: true},
}, "sensitive_value": {Type: cty.String, Optional: true, Sensitive: true},
Nesting: configschema.NestingSet,
},
"nesting_single": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"boop": {Type: cty.String, Optional: true, Sensitive: true},
},
},
Nesting: configschema.NestingSingle,
},
"nesting_nesting": {
Block: configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"nesting_list": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"value": {Type: cty.String, Optional: true},
"sensitive_value": {Type: cty.String, Optional: true, Sensitive: true},
},
},
Nesting: configschema.NestingList,
}, },
}, },
Nesting: configschema.NestingList,
},
"nesting_map": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Sensitive: true},
},
},
Nesting: configschema.NestingMap,
},
"nesting_set": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {Type: cty.String, Optional: true, Sensitive: true},
},
},
Nesting: configschema.NestingSet,
},
"nesting_single": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"boop": {Type: cty.String, Optional: true, Sensitive: true},
},
},
Nesting: configschema.NestingSingle,
},
"nesting_nesting": {
Block: configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"nesting_list": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"value": {Type: cty.String, Optional: true},
"sensitive_value": {Type: cty.String, Optional: true, Sensitive: true},
},
},
Nesting: configschema.NestingList,
},
},
},
Nesting: configschema.NestingSingle,
}, },
Nesting: configschema.NestingSingle,
}, },
}, },
}, },
@ -435,29 +437,30 @@ func TestEvaluatorGetResource_changes(t *testing.T) {
// Set up our schemas // Set up our schemas
schemas := &Schemas{ schemas := &Schemas{
Providers: map[addrs.Provider]*ProviderSchema{ Providers: map[addrs.Provider]providers.Schemas{
addrs.NewDefaultProvider("test"): { addrs.NewDefaultProvider("test"): {
Provider: &configschema.Block{}, ResourceTypes: map[string]providers.Schema{
ResourceTypes: map[string]*configschema.Block{
"test_resource": { "test_resource": {
Attributes: map[string]*configschema.Attribute{ Block: &configschema.Block{
"id": { Attributes: map[string]*configschema.Attribute{
Type: cty.String, "id": {
Computed: true, Type: cty.String,
}, Computed: true,
"to_mark_val": { },
Type: cty.String, "to_mark_val": {
Computed: true, Type: cty.String,
}, Computed: true,
"sensitive_value": { },
Type: cty.String, "sensitive_value": {
Computed: true, Type: cty.String,
Sensitive: true, Computed: true,
}, Sensitive: true,
"sensitive_collection": { },
Type: cty.Map(cty.String), "sensitive_collection": {
Computed: true, Type: cty.Map(cty.String),
Sensitive: true, Computed: true,
Sensitive: true,
},
}, },
}, },
}, },

View File

@ -13,6 +13,7 @@ import (
"github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/lang"
"github.com/hashicorp/terraform/internal/providers"
) )
func TestStaticValidateReferences(t *testing.T) { func TestStaticValidateReferences(t *testing.T) {
@ -83,23 +84,29 @@ For example, to correlate with indices of a referring resource, use:
cfg := testModule(t, "static-validate-refs") cfg := testModule(t, "static-validate-refs")
evaluator := &Evaluator{ evaluator := &Evaluator{
Config: cfg, Config: cfg,
Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]*ProviderSchema{ Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]providers.Schemas{
addrs.NewDefaultProvider("aws"): { addrs.NewDefaultProvider("aws"): {
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"aws_instance": {}, "aws_instance": {
Block: &configschema.Block{},
},
}, },
}, },
addrs.MustParseProviderSourceString("foobar/beep"): { addrs.MustParseProviderSourceString("foobar/beep"): {
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
// intentional mismatch between resource type prefix and provider type // intentional mismatch between resource type prefix and provider type
"boop_instance": {}, "boop_instance": {
Block: &configschema.Block{},
},
}, },
DataSources: map[string]*configschema.Block{ DataSources: map[string]providers.Schema{
"boop_data": { "boop_data": {
Attributes: map[string]*configschema.Attribute{ Block: &configschema.Block{
"id": { Attributes: map[string]*configschema.Attribute{
Type: cty.String, "id": {
Optional: true, Type: cty.String,
Optional: true,
},
}, },
}, },
}, },

View File

@ -467,12 +467,12 @@ func TestApplyGraphBuilder_updateFromOrphan(t *testing.T) {
cty.ObjectVal(map[string]cty.Value{ cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("b_id"), "id": cty.StringVal("b_id"),
"test_string": cty.StringVal("a_id"), "test_string": cty.StringVal("a_id"),
}), instanceSchema.ImpliedType()) }), instanceSchema.Block.ImpliedType())
bAfter, _ := plans.NewDynamicValue( bAfter, _ := plans.NewDynamicValue(
cty.ObjectVal(map[string]cty.Value{ cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("b_id"), "id": cty.StringVal("b_id"),
"test_string": cty.StringVal("changed"), "test_string": cty.StringVal("changed"),
}), instanceSchema.ImpliedType()) }), instanceSchema.Block.ImpliedType())
changes := &plans.Changes{ changes := &plans.Changes{
Resources: []*plans.ResourceInstanceChangeSrc{ Resources: []*plans.ResourceInstanceChangeSrc{
@ -572,12 +572,12 @@ func TestApplyGraphBuilder_updateFromCBDOrphan(t *testing.T) {
cty.ObjectVal(map[string]cty.Value{ cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("b_id"), "id": cty.StringVal("b_id"),
"test_string": cty.StringVal("a_id"), "test_string": cty.StringVal("a_id"),
}), instanceSchema.ImpliedType()) }), instanceSchema.Block.ImpliedType())
bAfter, _ := plans.NewDynamicValue( bAfter, _ := plans.NewDynamicValue(
cty.ObjectVal(map[string]cty.Value{ cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("b_id"), "id": cty.StringVal("b_id"),
"test_string": cty.StringVal("changed"), "test_string": cty.StringVal("changed"),
}), instanceSchema.ImpliedType()) }), instanceSchema.Block.ImpliedType())
changes := &plans.Changes{ changes := &plans.Changes{
Resources: []*plans.ResourceInstanceChangeSrc{ Resources: []*plans.ResourceInstanceChangeSrc{

View File

@ -54,7 +54,7 @@ type ContextGraphWalker struct {
variableValues map[string]map[string]cty.Value variableValues map[string]map[string]cty.Value
variableValuesLock sync.Mutex variableValuesLock sync.Mutex
providerCache map[string]providers.Interface providerCache map[string]providers.Interface
providerSchemas map[string]*ProviderSchema providerSchemas map[string]providers.Schemas
providerLock sync.Mutex providerLock sync.Mutex
provisionerCache map[string]provisioners.Interface provisionerCache map[string]provisioners.Interface
provisionerSchemas map[string]*configschema.Block provisionerSchemas map[string]*configschema.Block
@ -122,7 +122,7 @@ func (w *ContextGraphWalker) EvalContext() EvalContext {
func (w *ContextGraphWalker) init() { func (w *ContextGraphWalker) init() {
w.contexts = make(map[string]*BuiltinEvalContext) w.contexts = make(map[string]*BuiltinEvalContext)
w.providerCache = make(map[string]providers.Interface) w.providerCache = make(map[string]providers.Interface)
w.providerSchemas = make(map[string]*ProviderSchema) w.providerSchemas = make(map[string]providers.Schemas)
w.provisionerCache = make(map[string]provisioners.Interface) w.provisionerCache = make(map[string]provisioners.Interface)
w.provisionerSchemas = make(map[string]*configschema.Block) w.provisionerSchemas = make(map[string]*configschema.Block)
w.variableValues = make(map[string]map[string]cty.Value) w.variableValues = make(map[string]map[string]cty.Value)

View File

@ -137,7 +137,7 @@ func (n *NodeAbstractResourceInstance) AttachResourceState(s *states.Resource) {
// readDiff returns the planned change for a particular resource instance // readDiff returns the planned change for a particular resource instance
// object. // object.
func (n *NodeAbstractResourceInstance) readDiff(ctx EvalContext, providerSchema *ProviderSchema) (*plans.ResourceInstanceChange, error) { func (n *NodeAbstractResourceInstance) readDiff(ctx EvalContext, providerSchema providers.Schemas) (*plans.ResourceInstanceChange, error) {
changes := ctx.Changes() changes := ctx.Changes()
addr := n.ResourceInstanceAddr() addr := n.ResourceInstanceAddr()
@ -323,18 +323,13 @@ func (n *NodeAbstractResourceInstance) writeResourceInstanceStateImpl(ctx EvalCo
return nil return nil
} }
if providerSchema == nil {
// Should never happen, unless our state object is nil
panic("writeResourceInstanceStateImpl used with nil ProviderSchema")
}
if obj != nil { if obj != nil {
log.Printf("[TRACE] %s: writing state object for %s", logFuncName, absAddr) log.Printf("[TRACE] %s: writing state object for %s", logFuncName, absAddr)
} else { } else {
log.Printf("[TRACE] %s: removing state object for %s", logFuncName, absAddr) log.Printf("[TRACE] %s: removing state object for %s", logFuncName, absAddr)
} }
schema, currentVersion := (*providerSchema).SchemaForResourceAddr(absAddr.ContainingResource().Resource) schema, currentVersion := providerSchema.SchemaForResourceAddr(absAddr.ContainingResource().Resource)
if schema == nil { if schema == nil {
// It shouldn't be possible to get this far in any real scenario // It shouldn't be possible to get this far in any real scenario
// without a schema, but we might end up here in contrived tests that // without a schema, but we might end up here in contrived tests that
@ -663,10 +658,6 @@ func (n *NodeAbstractResourceInstance) plan(
return nil, nil, keyData, diags.Append(err) return nil, nil, keyData, diags.Append(err)
} }
if providerSchema == nil {
diags = diags.Append(fmt.Errorf("provider schema is unavailable for %s", n.Addr))
return nil, nil, keyData, diags
}
schema, _ := providerSchema.SchemaForResourceAddr(resource) schema, _ := providerSchema.SchemaForResourceAddr(resource)
if schema == nil { if schema == nil {
// Should be caught during validation, so we don't bother with a pretty error here // Should be caught during validation, so we don't bother with a pretty error here
@ -1416,10 +1407,6 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal
if diags.HasErrors() { if diags.HasErrors() {
return newVal, diags return newVal, diags
} }
if providerSchema == nil {
diags = diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
return newVal, diags
}
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource) schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
if schema == nil { if schema == nil {
// Should be caught during validation, so we don't bother with a pretty error here // Should be caught during validation, so we don't bother with a pretty error here
@ -1539,13 +1526,10 @@ func (n *NodeAbstractResourceInstance) providerMetas(ctx EvalContext) (cty.Value
if err != nil { if err != nil {
return metaConfigVal, diags.Append(err) return metaConfigVal, diags.Append(err)
} }
if providerSchema == nil {
return metaConfigVal, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
}
if n.ProviderMetas != nil { if n.ProviderMetas != nil {
if m, ok := n.ProviderMetas[n.ResolvedProvider.Provider]; ok && m != nil { if m, ok := n.ProviderMetas[n.ResolvedProvider.Provider]; ok && m != nil {
// if the provider doesn't support this feature, throw an error // if the provider doesn't support this feature, throw an error
if providerSchema.ProviderMeta == nil { if providerSchema.ProviderMeta.Block == nil {
diags = diags.Append(&hcl.Diagnostic{ diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,
Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ResolvedProvider.Provider.String()), Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ResolvedProvider.Provider.String()),
@ -1554,7 +1538,7 @@ func (n *NodeAbstractResourceInstance) providerMetas(ctx EvalContext) (cty.Value
}) })
} else { } else {
var configDiags tfdiags.Diagnostics var configDiags tfdiags.Diagnostics
metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, providerSchema.ProviderMeta, nil, EvalDataForNoInstanceKey) metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, providerSchema.ProviderMeta.Block, nil, EvalDataForNoInstanceKey)
diags = diags.Append(configDiags) diags = diags.Append(configDiags)
} }
} }
@ -1580,9 +1564,6 @@ func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, checkRule
if err != nil { if err != nil {
return nil, nil, keyData, diags.Append(err) return nil, nil, keyData, diags.Append(err)
} }
if providerSchema == nil {
return nil, nil, keyData, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
}
config := *n.Config config := *n.Config
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource) schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
@ -1851,10 +1832,6 @@ func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned
if err != nil { if err != nil {
return nil, keyData, diags.Append(err) return nil, keyData, diags.Append(err)
} }
if providerSchema == nil {
return nil, keyData, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
}
if planned != nil && planned.Action != plans.Read && planned.Action != plans.NoOp { if planned != nil && planned.Action != plans.Read && planned.Action != plans.NoOp {
// If any other action gets in here then that's always a bug; this // If any other action gets in here then that's always a bug; this
// EvalNode only deals with reading. // EvalNode only deals with reading.

View File

@ -171,7 +171,7 @@ func TestNodeAbstractResourceInstance_WriteResourceInstanceState(t *testing.T) {
}, },
} }
ctx.ProviderProvider = mockProvider ctx.ProviderProvider = mockProvider
ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() ctx.ProviderSchemaSchema = mockProvider.GetProviderSchema()
err := node.writeResourceInstanceState(ctx, obj, workingState) err := node.writeResourceInstanceState(ctx, obj, workingState)
if err != nil { if err != nil {

View File

@ -230,7 +230,7 @@ func TestNodeAbstractResource_ReadResourceInstanceState(t *testing.T) {
ctx := new(MockEvalContext) ctx := new(MockEvalContext)
ctx.StateState = test.State.SyncWrapper() ctx.StateState = test.State.SyncWrapper()
ctx.PathPath = addrs.RootModuleInstance ctx.PathPath = addrs.RootModuleInstance
ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() ctx.ProviderSchemaSchema = mockProvider.GetProviderSchema()
ctx.ProviderProvider = providers.Interface(mockProvider) ctx.ProviderProvider = providers.Interface(mockProvider)
@ -295,7 +295,7 @@ func TestNodeAbstractResource_ReadResourceInstanceStateDeposed(t *testing.T) {
ctx := new(MockEvalContext) ctx := new(MockEvalContext)
ctx.StateState = test.State.SyncWrapper() ctx.StateState = test.State.SyncWrapper()
ctx.PathPath = addrs.RootModuleInstance ctx.PathPath = addrs.RootModuleInstance
ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() ctx.ProviderSchemaSchema = mockProvider.GetProviderSchema()
ctx.ProviderProvider = providers.Interface(mockProvider) ctx.ProviderProvider = providers.Interface(mockProvider)
key := states.DeposedKey("00000001") // shim from legacy state assigns 0th deposed index this key key := states.DeposedKey("00000001") // shim from legacy state assigns 0th deposed index this key

View File

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/instances"
"github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/plans/objchange" "github.com/hashicorp/terraform/internal/plans/objchange"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags" "github.com/hashicorp/terraform/internal/tfdiags"
) )
@ -400,7 +401,7 @@ func (n *NodeApplyableResourceInstance) managedResourcePostconditions(ctx EvalCo
// Errors here are most often indicative of a bug in the provider, so our error // Errors here are most often indicative of a bug in the provider, so our error
// messages will report with that in mind. It's also possible that there's a bug // messages will report with that in mind. It's also possible that there's a bug
// in Terraform's Core's own "proposed new value" code in EvalDiff. // in Terraform's Core's own "proposed new value" code in EvalDiff.
func (n *NodeApplyableResourceInstance) checkPlannedChange(ctx EvalContext, plannedChange, actualChange *plans.ResourceInstanceChange, providerSchema *ProviderSchema) tfdiags.Diagnostics { func (n *NodeApplyableResourceInstance) checkPlannedChange(ctx EvalContext, plannedChange, actualChange *plans.ResourceInstanceChange, providerSchema providers.Schemas) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
addr := n.ResourceInstanceAddr().Resource addr := n.ResourceInstanceAddr().Resource

View File

@ -314,10 +314,6 @@ func (n *NodeDestroyDeposedResourceInstanceObject) writeResourceInstanceState(ct
if err != nil { if err != nil {
return err return err
} }
if providerSchema == nil {
// Should never happen, unless our state object is nil
panic("writeResourceInstanceStateDeposed used with no ProviderSchema object")
}
schema, currentVersion := providerSchema.SchemaForResourceAddr(absAddr.ContainingResource().Resource) schema, currentVersion := providerSchema.SchemaForResourceAddr(absAddr.ContainingResource().Resource)
if schema == nil { if schema == nil {

View File

@ -40,13 +40,15 @@ func TestNodePlanDeposedResourceInstanceObject_Execute(t *testing.T) {
PrevRunStateState: state.DeepCopy().SyncWrapper(), PrevRunStateState: state.DeepCopy().SyncWrapper(),
RefreshStateState: state.DeepCopy().SyncWrapper(), RefreshStateState: state.DeepCopy().SyncWrapper(),
ProviderProvider: p, ProviderProvider: p,
ProviderSchemaSchema: &ProviderSchema{ ProviderSchemaSchema: providers.Schemas{
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"test_instance": { "test_instance": {
Attributes: map[string]*configschema.Attribute{ Block: &configschema.Block{
"id": { Attributes: map[string]*configschema.Attribute{
Type: cty.String, "id": {
Computed: true, Type: cty.String,
Computed: true,
},
}, },
}, },
}, },
@ -96,13 +98,15 @@ func TestNodeDestroyDeposedResourceInstanceObject_Execute(t *testing.T) {
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
) )
schema := &ProviderSchema{ schema := providers.Schemas{
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"test_instance": { "test_instance": {
Attributes: map[string]*configschema.Attribute{ Block: &configschema.Block{
"id": { Attributes: map[string]*configschema.Attribute{
Type: cty.String, "id": {
Computed: true, Type: cty.String,
Computed: true,
},
}, },
}, },
}, },
@ -111,7 +115,7 @@ func TestNodeDestroyDeposedResourceInstanceObject_Execute(t *testing.T) {
p := testProvider("test") p := testProvider("test")
p.ConfigureProvider(providers.ConfigureProviderRequest{}) p.ConfigureProvider(providers.ConfigureProviderRequest{})
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) p.GetProviderSchemaResponse = &schema
p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{ p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{
UpgradedState: cty.ObjectVal(map[string]cty.Value{ UpgradedState: cty.ObjectVal(map[string]cty.Value{
@ -159,7 +163,7 @@ func TestNodeDestroyDeposedResourceInstanceObject_WriteResourceInstanceState(t *
}, },
}) })
ctx.ProviderProvider = mockProvider ctx.ProviderProvider = mockProvider
ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() ctx.ProviderSchemaSchema = mockProvider.GetProviderSchema()
obj := &states.ResourceInstanceObject{ obj := &states.ResourceInstanceObject{
Value: cty.ObjectVal(map[string]cty.Value{ Value: cty.ObjectVal(map[string]cty.Value{
@ -194,7 +198,7 @@ func TestNodeDestroyDeposedResourceInstanceObject_ExecuteMissingState(t *testing
ctx := &MockEvalContext{ ctx := &MockEvalContext{
StateState: states.NewState().SyncWrapper(), StateState: states.NewState().SyncWrapper(),
ProviderProvider: simpleMockProvider(), ProviderProvider: simpleMockProvider(),
ProviderSchemaSchema: p.ProviderSchema(), ProviderSchemaSchema: p.GetProviderSchema(),
ChangesChanges: plans.NewChanges().SyncWrapper(), ChangesChanges: plans.NewChanges().SyncWrapper(),
} }

View File

@ -439,7 +439,7 @@ func (n *NodePlannableResourceInstance) replaceTriggered(ctx EvalContext, repDat
return diags return diags
} }
func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.AbsResourceInstance, provider providers.Interface, providerSchema *ProviderSchema) (*states.ResourceInstanceObject, tfdiags.Diagnostics) { func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.AbsResourceInstance, provider providers.Interface, providerSchema providers.Schemas) (*states.ResourceInstanceObject, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
absAddr := addr.Resource.Absolute(ctx.Path()) absAddr := addr.Resource.Absolute(ctx.Path())

View File

@ -7,7 +7,6 @@ import (
"testing" "testing"
"github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/instances"
"github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/providers"
@ -43,9 +42,11 @@ func TestNodeResourcePlanOrphanExecute(t *testing.T) {
PrevRunStateState: state.DeepCopy().SyncWrapper(), PrevRunStateState: state.DeepCopy().SyncWrapper(),
InstanceExpanderExpander: instances.NewExpander(), InstanceExpanderExpander: instances.NewExpander(),
ProviderProvider: p, ProviderProvider: p,
ProviderSchemaSchema: &ProviderSchema{ ProviderSchemaSchema: providers.Schemas{
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"test_object": simpleTestSchema(), "test_object": {
Block: simpleTestSchema(),
},
}, },
}, },
ChangesChanges: plans.NewChanges().SyncWrapper(), ChangesChanges: plans.NewChanges().SyncWrapper(),
@ -107,9 +108,11 @@ func TestNodeResourcePlanOrphanExecute_alreadyDeleted(t *testing.T) {
PrevRunStateState: prevRunState.SyncWrapper(), PrevRunStateState: prevRunState.SyncWrapper(),
InstanceExpanderExpander: instances.NewExpander(), InstanceExpanderExpander: instances.NewExpander(),
ProviderProvider: p, ProviderProvider: p,
ProviderSchemaSchema: &ProviderSchema{ ProviderSchemaSchema: providers.Schemas{
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"test_object": simpleTestSchema(), "test_object": {
Block: simpleTestSchema(),
},
}, },
}, },
ChangesChanges: changes.SyncWrapper(), ChangesChanges: changes.SyncWrapper(),
@ -187,9 +190,11 @@ func TestNodeResourcePlanOrphanExecute_deposed(t *testing.T) {
PrevRunStateState: prevRunState.SyncWrapper(), PrevRunStateState: prevRunState.SyncWrapper(),
InstanceExpanderExpander: instances.NewExpander(), InstanceExpanderExpander: instances.NewExpander(),
ProviderProvider: p, ProviderProvider: p,
ProviderSchemaSchema: &ProviderSchema{ ProviderSchemaSchema: providers.Schemas{
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"test_object": simpleTestSchema(), "test_object": {
Block: simpleTestSchema(),
},
}, },
}, },
ChangesChanges: changes.SyncWrapper(), ChangesChanges: changes.SyncWrapper(),

View File

@ -276,10 +276,6 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
if diags.HasErrors() { if diags.HasErrors() {
return diags return diags
} }
if providerSchema == nil {
diags = diags.Append(fmt.Errorf("validateResource has nil schema for %s", n.Addr))
return diags
}
keyData := EvalDataForNoInstanceKey keyData := EvalDataForNoInstanceKey

View File

@ -193,7 +193,7 @@ func TestNodeValidatableResource_ValidateResource_managedResource(t *testing.T)
ctx := &MockEvalContext{} ctx := &MockEvalContext{}
ctx.installSimpleEval() ctx.installSimpleEval()
ctx.ProviderSchemaSchema = mp.ProviderSchema() ctx.ProviderSchemaSchema = mp.GetProviderSchema()
ctx.ProviderProvider = p ctx.ProviderProvider = p
err := node.validateResource(ctx) err := node.validateResource(ctx)
@ -223,7 +223,7 @@ func TestNodeValidatableResource_ValidateResource_managedResourceCount(t *testin
ctx := &MockEvalContext{} ctx := &MockEvalContext{}
ctx.installSimpleEval() ctx.installSimpleEval()
ctx.ProviderSchemaSchema = mp.ProviderSchema() ctx.ProviderSchemaSchema = mp.GetProviderSchema()
ctx.ProviderProvider = p ctx.ProviderProvider = p
tests := []struct { tests := []struct {
@ -307,7 +307,7 @@ func TestNodeValidatableResource_ValidateResource_dataSource(t *testing.T) {
ctx := &MockEvalContext{} ctx := &MockEvalContext{}
ctx.installSimpleEval() ctx.installSimpleEval()
ctx.ProviderSchemaSchema = mp.ProviderSchema() ctx.ProviderSchemaSchema = mp.GetProviderSchema()
ctx.ProviderProvider = p ctx.ProviderProvider = p
diags := node.validateResource(ctx) diags := node.validateResource(ctx)
@ -343,7 +343,7 @@ func TestNodeValidatableResource_ValidateResource_valid(t *testing.T) {
ctx := &MockEvalContext{} ctx := &MockEvalContext{}
ctx.installSimpleEval() ctx.installSimpleEval()
ctx.ProviderSchemaSchema = mp.ProviderSchema() ctx.ProviderSchemaSchema = mp.GetProviderSchema()
ctx.ProviderProvider = p ctx.ProviderProvider = p
diags := node.validateResource(ctx) diags := node.validateResource(ctx)
@ -380,7 +380,7 @@ func TestNodeValidatableResource_ValidateResource_warningsAndErrorsPassedThrough
ctx := &MockEvalContext{} ctx := &MockEvalContext{}
ctx.installSimpleEval() ctx.installSimpleEval()
ctx.ProviderSchemaSchema = mp.ProviderSchema() ctx.ProviderSchemaSchema = mp.GetProviderSchema()
ctx.ProviderProvider = p ctx.ProviderProvider = p
diags := node.validateResource(ctx) diags := node.validateResource(ctx)
@ -443,7 +443,7 @@ func TestNodeValidatableResource_ValidateResource_invalidDependsOn(t *testing.T)
ctx := &MockEvalContext{} ctx := &MockEvalContext{}
ctx.installSimpleEval() ctx.installSimpleEval()
ctx.ProviderSchemaSchema = mp.ProviderSchema() ctx.ProviderSchemaSchema = mp.GetProviderSchema()
ctx.ProviderProvider = p ctx.ProviderProvider = p
diags := node.validateResource(ctx) diags := node.validateResource(ctx)
@ -527,7 +527,7 @@ func TestNodeValidatableResource_ValidateResource_invalidIgnoreChangesNonexisten
ctx := &MockEvalContext{} ctx := &MockEvalContext{}
ctx.installSimpleEval() ctx.installSimpleEval()
ctx.ProviderSchemaSchema = mp.ProviderSchema() ctx.ProviderSchemaSchema = mp.GetProviderSchema()
ctx.ProviderProvider = p ctx.ProviderProvider = p
diags := node.validateResource(ctx) diags := node.validateResource(ctx)
@ -610,7 +610,7 @@ func TestNodeValidatableResource_ValidateResource_invalidIgnoreChangesComputed(t
ctx := &MockEvalContext{} ctx := &MockEvalContext{}
ctx.installSimpleEval() ctx.installSimpleEval()
ctx.ProviderSchemaSchema = mp.ProviderSchema() ctx.ProviderSchemaSchema = mp.GetProviderSchema()
ctx.ProviderProvider = p ctx.ProviderProvider = p
diags := node.validateResource(ctx) diags := node.validateResource(ctx)

View File

@ -11,7 +11,6 @@ import (
ctyjson "github.com/zclconf/go-cty/cty/json" ctyjson "github.com/zclconf/go-cty/cty/json"
"github.com/zclconf/go-cty/cty/msgpack" "github.com/zclconf/go-cty/cty/msgpack"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/configs/hcl2shim" "github.com/hashicorp/terraform/internal/configs/hcl2shim"
"github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/providers"
) )
@ -112,31 +111,6 @@ func (p *MockProvider) getProviderSchema() providers.GetProviderSchemaResponse {
} }
} }
// ProviderSchema is a helper to convert from the internal GetProviderSchemaResponse to
// a ProviderSchema.
func (p *MockProvider) ProviderSchema() *ProviderSchema {
resp := p.getProviderSchema()
schema := &ProviderSchema{
Provider: resp.Provider.Block,
ProviderMeta: resp.ProviderMeta.Block,
ResourceTypes: map[string]*configschema.Block{},
DataSources: map[string]*configschema.Block{},
ResourceTypeSchemaVersions: map[string]uint64{},
}
for resType, s := range resp.ResourceTypes {
schema.ResourceTypes[resType] = s.Block
schema.ResourceTypeSchemaVersions[resType] = uint64(s.Version)
}
for dataSource, s := range resp.DataSources {
schema.DataSources[dataSource] = s.Block
}
return schema
}
func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
p.Lock() p.Lock()
defer p.Unlock() defer p.Unlock()

View File

@ -49,30 +49,6 @@ func mockProviderWithResourceTypeSchema(name string, schema *configschema.Block)
} }
} }
// getProviderSchemaResponseFromProviderSchema is a test helper to convert a
// ProviderSchema to a GetProviderSchemaResponse for use when building a mock provider.
func getProviderSchemaResponseFromProviderSchema(providerSchema *ProviderSchema) *providers.GetProviderSchemaResponse {
resp := &providers.GetProviderSchemaResponse{
Provider: providers.Schema{Block: providerSchema.Provider},
ProviderMeta: providers.Schema{Block: providerSchema.ProviderMeta},
ResourceTypes: map[string]providers.Schema{},
DataSources: map[string]providers.Schema{},
}
for name, schema := range providerSchema.ResourceTypes {
resp.ResourceTypes[name] = providers.Schema{
Block: schema,
Version: int64(providerSchema.ResourceTypeSchemaVersions[name]),
}
}
for name, schema := range providerSchema.DataSources {
resp.DataSources[name] = providers.Schema{Block: schema}
}
return resp
}
// simpleMockProvider returns a MockProvider that is pre-configured // simpleMockProvider returns a MockProvider that is pre-configured
// with schema for its own config, for a resource type called "test_object" and // with schema for its own config, for a resource type called "test_object" and
// for a data source also called "test_object". // for a data source also called "test_object".
@ -103,3 +79,62 @@ func simpleMockProvider() *MockProvider {
}, },
} }
} }
// ProviderSchema is a helper to convert from the internal GetProviderSchemaResponse to
// a ProviderSchema.
func (p *MockProvider) ProviderSchema() *ProviderSchema {
resp := p.getProviderSchema()
schema := &ProviderSchema{
Provider: resp.Provider.Block,
ProviderMeta: resp.ProviderMeta.Block,
ResourceTypes: map[string]*configschema.Block{},
DataSources: map[string]*configschema.Block{},
ResourceTypeSchemaVersions: map[string]uint64{},
}
for resType, s := range resp.ResourceTypes {
schema.ResourceTypes[resType] = s.Block
schema.ResourceTypeSchemaVersions[resType] = uint64(s.Version)
}
for dataSource, s := range resp.DataSources {
schema.DataSources[dataSource] = s.Block
}
return schema
}
// the type was refactored out with all the functionality handled within the
// provider package, but we keep this here for a shim in existing tests.
type ProviderSchema struct {
Provider *configschema.Block
ProviderMeta *configschema.Block
ResourceTypes map[string]*configschema.Block
ResourceTypeSchemaVersions map[string]uint64
DataSources map[string]*configschema.Block
}
// getProviderSchemaResponseFromProviderSchema is a test helper to convert a
// ProviderSchema to a GetProviderSchemaResponse for use when building a mock provider.
func getProviderSchemaResponseFromProviderSchema(providerSchema *ProviderSchema) *providers.GetProviderSchemaResponse {
resp := &providers.GetProviderSchemaResponse{
Provider: providers.Schema{Block: providerSchema.Provider},
ProviderMeta: providers.Schema{Block: providerSchema.ProviderMeta},
ResourceTypes: map[string]providers.Schema{},
DataSources: map[string]providers.Schema{},
}
for name, schema := range providerSchema.ResourceTypes {
resp.ResourceTypes[name] = providers.Schema{
Block: schema,
Version: int64(providerSchema.ResourceTypeSchemaVersions[name]),
}
}
for name, schema := range providerSchema.DataSources {
resp.DataSources[name] = providers.Schema{Block: schema}
}
return resp
}

View File

@ -15,16 +15,10 @@ import (
"github.com/hashicorp/terraform/internal/tfdiags" "github.com/hashicorp/terraform/internal/tfdiags"
) )
// ProviderSchema is an alias for providers.Schemas, which is the new location
// for what we originally called terraform.ProviderSchema but which has
// moved out as part of ongoing refactoring to shrink down the main "terraform"
// package.
type ProviderSchema = providers.Schemas
// Schemas is a container for various kinds of schema that Terraform needs // Schemas is a container for various kinds of schema that Terraform needs
// during processing. // during processing.
type Schemas struct { type Schemas struct {
Providers map[addrs.Provider]*providers.Schemas Providers map[addrs.Provider]providers.Schemas
Provisioners map[string]*configschema.Block Provisioners map[string]*configschema.Block
} }
@ -33,21 +27,14 @@ type Schemas struct {
// //
// It's usually better to go use the more precise methods offered by type // It's usually better to go use the more precise methods offered by type
// Schemas to handle this detail automatically. // Schemas to handle this detail automatically.
func (ss *Schemas) ProviderSchema(provider addrs.Provider) *providers.Schemas { func (ss *Schemas) ProviderSchema(provider addrs.Provider) providers.Schemas {
if ss.Providers == nil {
return nil
}
return ss.Providers[provider] return ss.Providers[provider]
} }
// ProviderConfig returns the schema for the provider configuration of the // ProviderConfig returns the schema for the provider configuration of the
// given provider type, or nil if no such schema is available. // given provider type, or nil if no such schema is available.
func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block { func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block {
ps := ss.ProviderSchema(provider) return ss.ProviderSchema(provider).Provider.Block
if ps == nil {
return nil
}
return ps.Provider
} }
// ResourceTypeConfig returns the schema for the configuration of a given // ResourceTypeConfig returns the schema for the configuration of a given
@ -61,7 +48,7 @@ func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block {
// redundant. // redundant.
func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) { func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) {
ps := ss.ProviderSchema(provider) ps := ss.ProviderSchema(provider)
if ps == nil || ps.ResourceTypes == nil { if ps.ResourceTypes == nil {
return nil, 0 return nil, 0
} }
return ps.SchemaForResourceType(resourceMode, resourceType) return ps.SchemaForResourceType(resourceMode, resourceType)
@ -85,7 +72,7 @@ func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
// still valid but may be incomplete. // still valid but may be incomplete.
func loadSchemas(config *configs.Config, state *states.State, plugins *contextPlugins) (*Schemas, error) { func loadSchemas(config *configs.Config, state *states.State, plugins *contextPlugins) (*Schemas, error) {
schemas := &Schemas{ schemas := &Schemas{
Providers: map[addrs.Provider]*providers.Schemas{}, Providers: map[addrs.Provider]providers.Schemas{},
Provisioners: map[string]*configschema.Block{}, Provisioners: map[string]*configschema.Block{},
} }
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
@ -98,7 +85,7 @@ func loadSchemas(config *configs.Config, state *states.State, plugins *contextPl
return schemas, diags.Err() return schemas, diags.Err()
} }
func loadProviderSchemas(schemas map[addrs.Provider]*providers.Schemas, config *configs.Config, state *states.State, plugins *contextPlugins) tfdiags.Diagnostics { func loadProviderSchemas(schemas map[addrs.Provider]providers.Schemas, config *configs.Config, state *states.State, plugins *contextPlugins) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
ensure := func(fqn addrs.Provider) { ensure := func(fqn addrs.Provider) {
@ -114,7 +101,7 @@ func loadProviderSchemas(schemas map[addrs.Provider]*providers.Schemas, config *
// We'll put a stub in the map so we won't re-attempt this on // We'll put a stub in the map so we won't re-attempt this on
// future calls, which would then repeat the same error message // future calls, which would then repeat the same error message
// multiple times. // multiple times.
schemas[fqn] = &providers.Schemas{} schemas[fqn] = providers.Schemas{}
diags = diags.Append( diags = diags.Append(
tfdiags.Sourceless( tfdiags.Sourceless(
tfdiags.Error, tfdiags.Error,

View File

@ -14,8 +14,8 @@ func simpleTestSchemas() *Schemas {
provisioner := simpleMockProvisioner() provisioner := simpleMockProvisioner()
return &Schemas{ return &Schemas{
Providers: map[addrs.Provider]*ProviderSchema{ Providers: map[addrs.Provider]providers.Schemas{
addrs.NewDefaultProvider("test"): provider.ProviderSchema(), addrs.NewDefaultProvider("test"): provider.GetProviderSchema(),
}, },
Provisioners: map[string]*configschema.Block{ Provisioners: map[string]*configschema.Block{
"test": provisioner.GetSchemaResponse.Provisioner, "test": provisioner.GetSchemaResponse.Provisioner,
@ -31,32 +31,14 @@ func simpleTestSchemas() *Schemas {
// The intended use for this is in testing components that use schemas to // The intended use for this is in testing components that use schemas to
// drive other behavior, such as reference analysis during graph construction, // drive other behavior, such as reference analysis during graph construction,
// but that don't actually need to interact with providers otherwise. // but that don't actually need to interact with providers otherwise.
func schemaOnlyProvidersForTesting(schemas map[addrs.Provider]*ProviderSchema) *contextPlugins { func schemaOnlyProvidersForTesting(schemas map[addrs.Provider]providers.Schemas) *contextPlugins {
factories := make(map[addrs.Provider]providers.Factory, len(schemas)) factories := make(map[addrs.Provider]providers.Factory, len(schemas))
for providerAddr, schema := range schemas { for providerAddr, schema := range schemas {
schema := schema
resp := &providers.GetProviderSchemaResponse{
Provider: providers.Schema{
Block: schema.Provider,
},
ResourceTypes: make(map[string]providers.Schema),
DataSources: make(map[string]providers.Schema),
}
for t, tSchema := range schema.ResourceTypes {
resp.ResourceTypes[t] = providers.Schema{
Block: tSchema,
Version: int64(schema.ResourceTypeSchemaVersions[t]),
}
}
for t, tSchema := range schema.DataSources {
resp.DataSources[t] = providers.Schema{
Block: tSchema,
}
}
provider := &MockProvider{ provider := &MockProvider{
GetProviderSchemaResponse: resp, GetProviderSchemaResponse: &schema,
} }
factories[providerAddr] = func() (providers.Interface, error) { factories[providerAddr] = func() (providers.Interface, error) {

View File

@ -170,24 +170,24 @@ func testSetResourceInstanceTainted(module *states.Module, resource, attrsJson,
} }
func testProviderFuncFixed(rp providers.Interface) providers.Factory { func testProviderFuncFixed(rp providers.Interface) providers.Factory {
return func() (providers.Interface, error) { if p, ok := rp.(*MockProvider); ok {
if p, ok := rp.(*MockProvider); ok { // make sure none of the methods were "called" on this new instance
// make sure none of the methods were "called" on this new instance p.GetProviderSchemaCalled = false
p.GetProviderSchemaCalled = false p.ValidateProviderConfigCalled = false
p.ValidateProviderConfigCalled = false p.ValidateResourceConfigCalled = false
p.ValidateResourceConfigCalled = false p.ValidateDataResourceConfigCalled = false
p.ValidateDataResourceConfigCalled = false p.UpgradeResourceStateCalled = false
p.UpgradeResourceStateCalled = false p.ConfigureProviderCalled = false
p.ConfigureProviderCalled = false p.StopCalled = false
p.StopCalled = false p.ReadResourceCalled = false
p.ReadResourceCalled = false p.PlanResourceChangeCalled = false
p.PlanResourceChangeCalled = false p.ApplyResourceChangeCalled = false
p.ApplyResourceChangeCalled = false p.ImportResourceStateCalled = false
p.ImportResourceStateCalled = false p.ReadDataSourceCalled = false
p.ReadDataSourceCalled = false p.CloseCalled = false
p.CloseCalled = false }
}
return func() (providers.Interface, error) {
return rp, nil return rp, nil
} }
} }

View File

@ -72,13 +72,15 @@ func TestGraphNodeImportStateSubExecute(t *testing.T) {
ctx := &MockEvalContext{ ctx := &MockEvalContext{
StateState: state.SyncWrapper(), StateState: state.SyncWrapper(),
ProviderProvider: provider, ProviderProvider: provider,
ProviderSchemaSchema: &ProviderSchema{ ProviderSchemaSchema: providers.Schemas{
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"aws_instance": { "aws_instance": {
Attributes: map[string]*configschema.Attribute{ Block: &configschema.Block{
"id": { Attributes: map[string]*configschema.Attribute{
Type: cty.String, "id": {
Computed: true, Type: cty.String,
Computed: true,
},
}, },
}, },
}, },
@ -132,13 +134,15 @@ func TestGraphNodeImportStateSubExecuteNull(t *testing.T) {
ctx := &MockEvalContext{ ctx := &MockEvalContext{
StateState: state.SyncWrapper(), StateState: state.SyncWrapper(),
ProviderProvider: provider, ProviderProvider: provider,
ProviderSchemaSchema: &ProviderSchema{ ProviderSchemaSchema: providers.Schemas{
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"aws_instance": { "aws_instance": {
Attributes: map[string]*configschema.Attribute{ Block: &configschema.Block{
"id": { Attributes: map[string]*configschema.Attribute{
Type: cty.String, "id": {
Computed: true, Type: cty.String,
Computed: true,
},
}, },
}, },
}, },

View File

@ -9,6 +9,7 @@ import (
"github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/providers"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
) )
@ -33,18 +34,20 @@ func TestTransitiveReductionTransformer(t *testing.T) {
{ {
transform := &AttachSchemaTransformer{ transform := &AttachSchemaTransformer{
Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]*ProviderSchema{ Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]providers.Schemas{
addrs.NewDefaultProvider("aws"): { addrs.NewDefaultProvider("aws"): {
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"aws_instance": { "aws_instance": {
Attributes: map[string]*configschema.Attribute{ Block: &configschema.Block{
"A": { Attributes: map[string]*configschema.Attribute{
Type: cty.String, "A": {
Optional: true, Type: cty.String,
}, Optional: true,
"B": { },
Type: cty.String, "B": {
Optional: true, Type: cty.String,
Optional: true,
},
}, },
}, },
}, },

View File

@ -11,12 +11,13 @@ import (
"github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/lang"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/tfdiags" "github.com/hashicorp/terraform/internal/tfdiags"
) )
// validateSelfRef checks to ensure that expressions within a particular // validateSelfRef checks to ensure that expressions within a particular
// referencable block do not reference that same block. // referencable block do not reference that same block.
func validateSelfRef(addr addrs.Referenceable, config hcl.Body, providerSchema *ProviderSchema) tfdiags.Diagnostics { func validateSelfRef(addr addrs.Referenceable, config hcl.Body, providerSchema providers.Schemas) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
addrStrs := make([]string, 0, 1) addrStrs := make([]string, 0, 1)
@ -27,11 +28,6 @@ func validateSelfRef(addr addrs.Referenceable, config hcl.Body, providerSchema *
addrStrs = append(addrStrs, tAddr.ContainingResource().String()) addrStrs = append(addrStrs, tAddr.ContainingResource().String())
} }
if providerSchema == nil {
diags = diags.Append(fmt.Errorf("provider schema unavailable while validating %s for self-references; this is a bug in Terraform and should be reported", addr))
return diags
}
var schema *configschema.Block var schema *configschema.Block
switch tAddr := addr.(type) { switch tAddr := addr.(type) {
case addrs.Resource: case addrs.Resource:

View File

@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hcltest" "github.com/hashicorp/hcl/v2/hcltest"
@ -82,13 +83,15 @@ func TestValidateSelfRef(t *testing.T) {
}, },
}) })
ps := &ProviderSchema{ ps := providers.Schemas{
ResourceTypes: map[string]*configschema.Block{ ResourceTypes: map[string]providers.Schema{
"aws_instance": &configschema.Block{ "aws_instance": {
Attributes: map[string]*configschema.Attribute{ Block: &configschema.Block{
"foo": { Attributes: map[string]*configschema.Attribute{
Type: cty.String, "foo": {
Required: true, Type: cty.String,
Required: true,
},
}, },
}, },
}, },