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) {
// 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
// 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

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) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
m := req.ProposedNewState.AsValueMap()
m["output"] = cty.StringVal("bar")
@ -2820,6 +2820,12 @@ resource "test_resource" "a" {
})
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{
Mode: plans.NormalMode,
SetVariables: InputValues{
@ -2841,6 +2847,12 @@ resource "test_resource" "a" {
})
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) {
s.SetResourceInstanceCurrent(mustResourceInstanceAddr("test_resource.a"), &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"value":"boop","output":"blorp"}`),
@ -2869,6 +2881,12 @@ resource "test_resource" "a" {
})
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) {
m := req.ProposedNewState.AsValueMap()
m["output"] = cty.StringVal("")
@ -2898,6 +2916,12 @@ resource "test_resource" "a" {
})
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) {
s.SetResourceInstanceCurrent(mustResourceInstanceAddr("test_resource.a"), &states.ResourceInstanceObjectSrc{
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) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
state := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(mustResourceInstanceAddr("test_resource.a"), &states.ResourceInstanceObjectSrc{
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) {
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("boop"),
@ -3106,6 +3135,11 @@ resource "test_resource" "a" {
})
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{
Mode: plans.NormalMode,
SetVariables: InputValues{
@ -3127,6 +3161,11 @@ resource "test_resource" "a" {
})
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{
Mode: plans.RefreshOnlyMode,
SetVariables: InputValues{
@ -3160,6 +3199,11 @@ resource "test_resource" "a" {
})
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{
State: cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("boop"),
@ -3187,6 +3231,11 @@ resource "test_resource" "a" {
})
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{
State: cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("boop"),
@ -3223,6 +3272,11 @@ resource "test_resource" "a" {
})
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{
State: cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("nope"),

View File

@ -6,7 +6,6 @@ package terraform
import (
"fmt"
"log"
"sync"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema"
@ -21,15 +20,6 @@ import (
type contextPlugins struct {
providerFactories map[addrs.Provider]providers.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 {
@ -37,15 +27,9 @@ func newContextPlugins(providerFactories map[addrs.Provider]providers.Factory, p
providerFactories: providerFactories,
provisionerFactories: provisionerFactories,
}
ret.init()
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 {
_, ok := cp.providerFactories[addr]
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
// to repeatedly call this method with the same address if various different
// parts of Terraform all need the same schema information.
func (cp *contextPlugins) ProviderSchema(addr addrs.Provider) (*ProviderSchema, error) {
cp.schemasLock.Lock()
defer cp.schemasLock.Unlock()
if schema, ok := cp.providerSchemas[addr]; ok {
return schema, nil
}
func (cp *contextPlugins) ProviderSchema(addr addrs.Provider) (providers.Schemas, error) {
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)
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()
resp := provider.GetProviderSchema()
if resp.Diagnostics.HasErrors() {
return nil, 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),
return resp, fmt.Errorf("failed to retrieve schema from provider %q: %s", addr, resp.Diagnostics.Err())
}
if resp.Provider.Version < 0 {
// We're not using the version numbers here yet, but we'll check
// 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 {
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 {
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 {
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 {
// We're not using the version numbers here yet, but we'll check
// 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 {
s.ProviderMeta = resp.ProviderMeta.Block
}
cp.providerSchemas[addr] = s
return s, nil
return resp, nil
}
// 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 providerSchema.Provider, nil
return providerSchema.Provider.Block, nil
}
// 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
// parts of Terraform all need the same schema information.
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)
provisioner, err := cp.NewProvisionerInstance(typ)
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())
}
cp.provisionerSchemas[typ] = resp.Provisioner
return resp.Provisioner, nil
}

View File

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

View File

@ -57,7 +57,7 @@ type EvalContext interface {
//
// This method expects an _absolute_ provider configuration address, since
// 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.
//

View File

@ -149,7 +149,22 @@ func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.
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)
}
@ -181,16 +196,6 @@ func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, c
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{
TerraformVersion: version.String(),
Config: cfg,

View File

@ -46,7 +46,7 @@ type MockEvalContext struct {
ProviderSchemaCalled bool
ProviderSchemaAddr addrs.AbsProviderConfig
ProviderSchemaSchema *ProviderSchema
ProviderSchemaSchema providers.Schemas
ProviderSchemaError error
CloseProviderCalled bool
@ -190,7 +190,7 @@ func (c *MockEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Inter
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.ProviderSchemaAddr = addr
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.
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 == "" {
// Should never happen
panic("GetProvider used with uninitialized provider configuration address")
}
provider := ctx.Provider(addr)
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
// schema to the callers.
schema, err := ctx.ProviderSchema(addr)
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
}

View File

@ -15,6 +15,7 @@ import (
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/lang/marks"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
)
@ -264,71 +265,72 @@ func TestEvaluatorGetResource(t *testing.T) {
},
},
State: stateSync,
Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]*ProviderSchema{
Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]providers.Schemas{
addrs.NewDefaultProvider("test"): {
Provider: &configschema.Block{},
ResourceTypes: map[string]*configschema.Block{
ResourceTypes: map[string]providers.Schema{
"test_resource": {
Attributes: map[string]*configschema.Attribute{
"id": {
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},
},
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Computed: true,
},
Nesting: configschema.NestingList,
},
"nesting_map": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Sensitive: true},
},
"value": {
Type: cty.String,
Computed: 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,
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_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
schemas := &Schemas{
Providers: map[addrs.Provider]*ProviderSchema{
Providers: map[addrs.Provider]providers.Schemas{
addrs.NewDefaultProvider("test"): {
Provider: &configschema.Block{},
ResourceTypes: map[string]*configschema.Block{
ResourceTypes: map[string]providers.Schema{
"test_resource": {
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Computed: true,
},
"to_mark_val": {
Type: cty.String,
Computed: true,
},
"sensitive_value": {
Type: cty.String,
Computed: true,
Sensitive: true,
},
"sensitive_collection": {
Type: cty.Map(cty.String),
Computed: true,
Sensitive: true,
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Computed: true,
},
"to_mark_val": {
Type: cty.String,
Computed: true,
},
"sensitive_value": {
Type: cty.String,
Computed: true,
Sensitive: true,
},
"sensitive_collection": {
Type: cty.Map(cty.String),
Computed: true,
Sensitive: true,
},
},
},
},

View File

@ -13,6 +13,7 @@ import (
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/lang"
"github.com/hashicorp/terraform/internal/providers"
)
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")
evaluator := &Evaluator{
Config: cfg,
Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]*ProviderSchema{
Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]providers.Schemas{
addrs.NewDefaultProvider("aws"): {
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {},
ResourceTypes: map[string]providers.Schema{
"aws_instance": {
Block: &configschema.Block{},
},
},
},
addrs.MustParseProviderSourceString("foobar/beep"): {
ResourceTypes: map[string]*configschema.Block{
ResourceTypes: map[string]providers.Schema{
// 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": {
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Optional: true,
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Optional: true,
},
},
},
},

View File

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

View File

@ -54,7 +54,7 @@ type ContextGraphWalker struct {
variableValues map[string]map[string]cty.Value
variableValuesLock sync.Mutex
providerCache map[string]providers.Interface
providerSchemas map[string]*ProviderSchema
providerSchemas map[string]providers.Schemas
providerLock sync.Mutex
provisionerCache map[string]provisioners.Interface
provisionerSchemas map[string]*configschema.Block
@ -122,7 +122,7 @@ func (w *ContextGraphWalker) EvalContext() EvalContext {
func (w *ContextGraphWalker) init() {
w.contexts = make(map[string]*BuiltinEvalContext)
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.provisionerSchemas = make(map[string]*configschema.Block)
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
// 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()
addr := n.ResourceInstanceAddr()
@ -323,18 +323,13 @@ func (n *NodeAbstractResourceInstance) writeResourceInstanceStateImpl(ctx EvalCo
return nil
}
if providerSchema == nil {
// Should never happen, unless our state object is nil
panic("writeResourceInstanceStateImpl used with nil ProviderSchema")
}
if obj != nil {
log.Printf("[TRACE] %s: writing state object for %s", logFuncName, absAddr)
} else {
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 {
// 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
@ -663,10 +658,6 @@ func (n *NodeAbstractResourceInstance) plan(
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)
if schema == nil {
// 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() {
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)
if schema == nil {
// 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 {
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 m, ok := n.ProviderMetas[n.ResolvedProvider.Provider]; ok && m != nil {
// 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{
Severity: hcl.DiagError,
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 {
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)
}
}
@ -1580,9 +1564,6 @@ func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, checkRule
if err != nil {
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
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
@ -1851,10 +1832,6 @@ func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned
if err != nil {
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 any other action gets in here then that's always a bug; this
// EvalNode only deals with reading.

View File

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

View File

@ -230,7 +230,7 @@ func TestNodeAbstractResource_ReadResourceInstanceState(t *testing.T) {
ctx := new(MockEvalContext)
ctx.StateState = test.State.SyncWrapper()
ctx.PathPath = addrs.RootModuleInstance
ctx.ProviderSchemaSchema = mockProvider.ProviderSchema()
ctx.ProviderSchemaSchema = mockProvider.GetProviderSchema()
ctx.ProviderProvider = providers.Interface(mockProvider)
@ -295,7 +295,7 @@ func TestNodeAbstractResource_ReadResourceInstanceStateDeposed(t *testing.T) {
ctx := new(MockEvalContext)
ctx.StateState = test.State.SyncWrapper()
ctx.PathPath = addrs.RootModuleInstance
ctx.ProviderSchemaSchema = mockProvider.ProviderSchema()
ctx.ProviderSchemaSchema = mockProvider.GetProviderSchema()
ctx.ProviderProvider = providers.Interface(mockProvider)
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/plans"
"github.com/hashicorp/terraform/internal/plans/objchange"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states"
"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
// 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.
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
addr := n.ResourceInstanceAddr().Resource

View File

@ -314,10 +314,6 @@ func (n *NodeDestroyDeposedResourceInstanceObject) writeResourceInstanceState(ct
if err != nil {
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)
if schema == nil {

View File

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

View File

@ -439,7 +439,7 @@ func (n *NodePlannableResourceInstance) replaceTriggered(ctx EvalContext, repDat
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
absAddr := addr.Resource.Absolute(ctx.Path())

View File

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

View File

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

View File

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

View File

@ -11,7 +11,6 @@ import (
ctyjson "github.com/zclconf/go-cty/cty/json"
"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/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) {
p.Lock()
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
// with schema for its own config, for a resource type called "test_object" and
// 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"
)
// 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
// during processing.
type Schemas struct {
Providers map[addrs.Provider]*providers.Schemas
Providers map[addrs.Provider]providers.Schemas
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
// Schemas to handle this detail automatically.
func (ss *Schemas) ProviderSchema(provider addrs.Provider) *providers.Schemas {
if ss.Providers == nil {
return nil
}
func (ss *Schemas) ProviderSchema(provider addrs.Provider) providers.Schemas {
return ss.Providers[provider]
}
// ProviderConfig returns the schema for the provider configuration of the
// given provider type, or nil if no such schema is available.
func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block {
ps := ss.ProviderSchema(provider)
if ps == nil {
return nil
}
return ps.Provider
return ss.ProviderSchema(provider).Provider.Block
}
// ResourceTypeConfig returns the schema for the configuration of a given
@ -61,7 +48,7 @@ func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block {
// redundant.
func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) {
ps := ss.ProviderSchema(provider)
if ps == nil || ps.ResourceTypes == nil {
if ps.ResourceTypes == nil {
return nil, 0
}
return ps.SchemaForResourceType(resourceMode, resourceType)
@ -85,7 +72,7 @@ func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
// still valid but may be incomplete.
func loadSchemas(config *configs.Config, state *states.State, plugins *contextPlugins) (*Schemas, error) {
schemas := &Schemas{
Providers: map[addrs.Provider]*providers.Schemas{},
Providers: map[addrs.Provider]providers.Schemas{},
Provisioners: map[string]*configschema.Block{},
}
var diags tfdiags.Diagnostics
@ -98,7 +85,7 @@ func loadSchemas(config *configs.Config, state *states.State, plugins *contextPl
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
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
// future calls, which would then repeat the same error message
// multiple times.
schemas[fqn] = &providers.Schemas{}
schemas[fqn] = providers.Schemas{}
diags = diags.Append(
tfdiags.Sourceless(
tfdiags.Error,

View File

@ -14,8 +14,8 @@ func simpleTestSchemas() *Schemas {
provisioner := simpleMockProvisioner()
return &Schemas{
Providers: map[addrs.Provider]*ProviderSchema{
addrs.NewDefaultProvider("test"): provider.ProviderSchema(),
Providers: map[addrs.Provider]providers.Schemas{
addrs.NewDefaultProvider("test"): provider.GetProviderSchema(),
},
Provisioners: map[string]*configschema.Block{
"test": provisioner.GetSchemaResponse.Provisioner,
@ -31,32 +31,14 @@ func simpleTestSchemas() *Schemas {
// The intended use for this is in testing components that use schemas to
// drive other behavior, such as reference analysis during graph construction,
// 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))
for providerAddr, schema := range schemas {
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,
}
}
schema := schema
provider := &MockProvider{
GetProviderSchemaResponse: resp,
GetProviderSchemaResponse: &schema,
}
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 {
return func() (providers.Interface, error) {
if p, ok := rp.(*MockProvider); ok {
// make sure none of the methods were "called" on this new instance
p.GetProviderSchemaCalled = false
p.ValidateProviderConfigCalled = false
p.ValidateResourceConfigCalled = false
p.ValidateDataResourceConfigCalled = false
p.UpgradeResourceStateCalled = false
p.ConfigureProviderCalled = false
p.StopCalled = false
p.ReadResourceCalled = false
p.PlanResourceChangeCalled = false
p.ApplyResourceChangeCalled = false
p.ImportResourceStateCalled = false
p.ReadDataSourceCalled = false
p.CloseCalled = false
}
if p, ok := rp.(*MockProvider); ok {
// make sure none of the methods were "called" on this new instance
p.GetProviderSchemaCalled = false
p.ValidateProviderConfigCalled = false
p.ValidateResourceConfigCalled = false
p.ValidateDataResourceConfigCalled = false
p.UpgradeResourceStateCalled = false
p.ConfigureProviderCalled = false
p.StopCalled = false
p.ReadResourceCalled = false
p.PlanResourceChangeCalled = false
p.ApplyResourceChangeCalled = false
p.ImportResourceStateCalled = false
p.ReadDataSourceCalled = false
p.CloseCalled = false
}
return func() (providers.Interface, error) {
return rp, nil
}
}

View File

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

View File

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

View File

@ -11,12 +11,13 @@ import (
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/lang"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// validateSelfRef checks to ensure that expressions within a particular
// 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
addrStrs := make([]string, 0, 1)
@ -27,11 +28,6 @@ func validateSelfRef(addr addrs.Referenceable, config hcl.Body, providerSchema *
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
switch tAddr := addr.(type) {
case addrs.Resource:

View File

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