core: Stop loading provider schema during graph walk
We now fetch all of the necessary schemas during context creation, so we can just thread that repository of schemas through into EvalContext and Evaluator and access the schemas as needed without any further fetching. This requires updating a few tests to have a valid Provider address in their state objects, because we need that in order to trigger the loading of the relevant schema.
This commit is contained in:
parent
555cd977f8
commit
d961b1de1b
|
@ -285,6 +285,7 @@ func TestContext2Apply_resourceDependsOnModuleStateOnly(t *testing.T) {
|
|||
ID: "bar",
|
||||
},
|
||||
Dependencies: []string{"module.child"},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -296,6 +297,7 @@ func TestContext2Apply_resourceDependsOnModuleStateOnly(t *testing.T) {
|
|||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1330,6 +1332,7 @@ func testContext2Apply_destroyDependsOnStateOnly(t *testing.T) {
|
|||
ID: "foo",
|
||||
Attributes: map[string]string{},
|
||||
},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
|
||||
"aws_instance.bar": &ResourceState{
|
||||
|
@ -1339,6 +1342,7 @@ func testContext2Apply_destroyDependsOnStateOnly(t *testing.T) {
|
|||
Attributes: map[string]string{},
|
||||
},
|
||||
Dependencies: []string{"aws_instance.foo"},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1408,6 +1412,7 @@ func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) {
|
|||
ID: "foo",
|
||||
Attributes: map[string]string{},
|
||||
},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
|
||||
"aws_instance.bar": &ResourceState{
|
||||
|
@ -1417,6 +1422,7 @@ func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) {
|
|||
Attributes: map[string]string{},
|
||||
},
|
||||
Dependencies: []string{"aws_instance.foo"},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -6158,6 +6164,7 @@ func TestContext2Apply_destroyNestedModule(t *testing.T) {
|
|||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -6207,6 +6214,7 @@ func TestContext2Apply_destroyDeeplyNestedModule(t *testing.T) {
|
|||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -6977,6 +6985,7 @@ func TestContext2Apply_hookOrphan(t *testing.T) {
|
|||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -853,6 +853,7 @@ func TestContext2Refresh_dataOrphan(t *testing.T) {
|
|||
Primary: &InstanceState{
|
||||
ID: "foo",
|
||||
},
|
||||
Provider: "provider.null",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1224,6 +1225,7 @@ func TestContext2Refresh_orphanModule(t *testing.T) {
|
|||
"module.child",
|
||||
"module.child",
|
||||
},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1241,6 +1243,7 @@ func TestContext2Refresh_orphanModule(t *testing.T) {
|
|||
Dependencies: []string{
|
||||
"module.grandchild",
|
||||
},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
Outputs: map[string]*OutputState{
|
||||
|
@ -1262,6 +1265,7 @@ func TestContext2Refresh_orphanModule(t *testing.T) {
|
|||
Primary: &InstanceState{
|
||||
ID: "i-cde345",
|
||||
},
|
||||
Provider: "provider.aws",
|
||||
},
|
||||
},
|
||||
Outputs: map[string]*OutputState{
|
||||
|
|
|
@ -28,6 +28,15 @@ type BuiltinEvalContext struct {
|
|||
// eval context.
|
||||
Evaluator *Evaluator
|
||||
|
||||
// Schemas is a repository of all of the schemas we should need to
|
||||
// decode configuration blocks and expressions. This must be constructed by
|
||||
// the caller to include schemas for all of the providers, resource types,
|
||||
// data sources and provisioners used by the given configuration and
|
||||
// state.
|
||||
//
|
||||
// This must not be mutated during evaluation.
|
||||
Schemas *Schemas
|
||||
|
||||
// VariableValues contains the variable values across all modules. This
|
||||
// structure is shared across the entire containing context, and so it
|
||||
// may be accessed only when holding VariableValuesLock.
|
||||
|
@ -41,11 +50,9 @@ type BuiltinEvalContext struct {
|
|||
Hooks []Hook
|
||||
InputValue UIInput
|
||||
ProviderCache map[string]ResourceProvider
|
||||
ProviderSchemas map[string]*ProviderSchema
|
||||
ProviderInputConfig map[string]map[string]cty.Value
|
||||
ProviderLock *sync.Mutex
|
||||
ProvisionerCache map[string]ResourceProvisioner
|
||||
ProvisionerSchemas map[string]*configschema.Block
|
||||
ProvisionerLock *sync.Mutex
|
||||
DiffValue *Diff
|
||||
DiffLock *sync.RWMutex
|
||||
|
@ -115,34 +122,6 @@ func (ctx *BuiltinEvalContext) InitProvider(typeName string, addr addrs.Provider
|
|||
log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", typeName, absAddr)
|
||||
ctx.ProviderCache[key] = p
|
||||
|
||||
// Also fetch and cache the provider's schema.
|
||||
// FIXME: This is using a non-ideal provider API that requires us to
|
||||
// request specific resource types, but we actually just want _all_ the
|
||||
// resource types, so we'll list these first. Once the provider API is
|
||||
// updated we'll get enough data to populate this whole structure in
|
||||
// a single call.
|
||||
resourceTypes := p.Resources()
|
||||
dataSources := p.DataSources()
|
||||
resourceTypeNames := make([]string, len(resourceTypes))
|
||||
for i, t := range resourceTypes {
|
||||
resourceTypeNames[i] = t.Name
|
||||
}
|
||||
dataSourceNames := make([]string, len(dataSources))
|
||||
for i, t := range dataSources {
|
||||
dataSourceNames[i] = t.Name
|
||||
}
|
||||
schema, err := p.GetSchema(&ProviderSchemaRequest{
|
||||
DataSources: dataSourceNames,
|
||||
ResourceTypes: resourceTypeNames,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching schema for %s: %s", key, err)
|
||||
}
|
||||
if ctx.ProviderSchemas == nil {
|
||||
ctx.ProviderSchemas = make(map[string]*ProviderSchema)
|
||||
}
|
||||
ctx.ProviderSchemas[typeName] = schema
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -158,10 +137,7 @@ func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) ResourcePr
|
|||
func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *ProviderSchema {
|
||||
ctx.once.Do(ctx.init)
|
||||
|
||||
ctx.ProviderLock.Lock()
|
||||
defer ctx.ProviderLock.Unlock()
|
||||
|
||||
return ctx.ProviderSchemas[addr.ProviderConfig.Type]
|
||||
return ctx.Schemas.ProviderSchema(addr.ProviderConfig.Type)
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.ProviderConfig) error {
|
||||
|
@ -250,16 +226,6 @@ func (ctx *BuiltinEvalContext) InitProvisioner(n string) (ResourceProvisioner, e
|
|||
|
||||
ctx.ProvisionerCache[key] = p
|
||||
|
||||
// Also fetch the provisioner's schema
|
||||
schema, err := p.GetConfigSchema()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting schema for provisioner %q: %s", n, err)
|
||||
}
|
||||
if ctx.ProvisionerSchemas == nil {
|
||||
ctx.ProvisionerSchemas = make(map[string]*configschema.Block)
|
||||
}
|
||||
ctx.ProvisionerSchemas[n] = schema
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -276,10 +242,7 @@ func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner {
|
|||
func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) *configschema.Block {
|
||||
ctx.once.Do(ctx.init)
|
||||
|
||||
ctx.ProvisionerLock.Lock()
|
||||
defer ctx.ProvisionerLock.Unlock()
|
||||
|
||||
return ctx.ProvisionerSchemas[n]
|
||||
return ctx.Schemas.ProvisionerConfig(n)
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error {
|
||||
|
|
|
@ -7,9 +7,6 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/hashicorp/terraform/config/configschema"
|
||||
)
|
||||
|
||||
func TestBuiltinEvalContextProviderInput(t *testing.T) {
|
||||
|
@ -61,15 +58,6 @@ func TestBuildingEvalContextInitProvider(t *testing.T) {
|
|||
SchemaAvailable: true,
|
||||
},
|
||||
},
|
||||
GetSchemaReturn: &ProviderSchema{
|
||||
Provider: &configschema.Block{},
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"test_thing": &configschema.Block{},
|
||||
},
|
||||
DataSources: map[string]*configschema.Block{
|
||||
"test_thing": &configschema.Block{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := testBuiltinEvalContext(t)
|
||||
|
@ -92,32 +80,6 @@ func TestBuildingEvalContextInitProvider(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("error initializing provider test.foo: %s", err)
|
||||
}
|
||||
|
||||
{
|
||||
got := testP.GetSchemaRequest
|
||||
want := &ProviderSchemaRequest{
|
||||
DataSources: []string{"test_thing"},
|
||||
ResourceTypes: []string{"test_thing"},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong schema request\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want))
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
schema := ctx.ProviderSchema(providerAddrDefault.Absolute(addrs.RootModuleInstance))
|
||||
if got, want := schema, testP.GetSchemaReturn; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong schema\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want))
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
schema := ctx.ProviderSchema(providerAddrAlias.Absolute(addrs.RootModuleInstance))
|
||||
if got, want := schema, testP.GetSchemaReturn; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong schema\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBuiltinEvalContext(t *testing.T) *BuiltinEvalContext {
|
||||
|
|
|
@ -44,11 +44,13 @@ type Evaluator struct {
|
|||
VariableValues map[string]map[string]cty.Value
|
||||
VariableValuesLock *sync.Mutex
|
||||
|
||||
// ProviderSchemas is a map of schemas for all provider configurations
|
||||
// that have been initialized so far. This is mutated concurrently, so
|
||||
// it must be accessed only while holding ProvidersLock.
|
||||
ProviderSchemas map[string]*ProviderSchema
|
||||
ProvidersLock *sync.Mutex
|
||||
// Schemas is a repository of all of the schemas we should need to
|
||||
// evaluate expressions. This must be constructed by the caller to
|
||||
// include schemas for all of the providers, resource types, data sources
|
||||
// and provisioners used by the given configuration and state.
|
||||
//
|
||||
// This must not be mutated during evaluation.
|
||||
Schemas *Schemas
|
||||
|
||||
// State is the current state. During some operations this structure
|
||||
// is mutated concurrently, and so it must be accessed only while holding
|
||||
|
@ -694,23 +696,18 @@ func (d *evaluationStateData) getResourceInstancePending(addr addrs.ResourceInst
|
|||
}
|
||||
|
||||
func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAddr addrs.AbsProviderConfig) *configschema.Block {
|
||||
d.Evaluator.ProvidersLock.Lock()
|
||||
defer d.Evaluator.ProvidersLock.Unlock()
|
||||
|
||||
log.Printf("[TRACE] Need provider schema for %s", providerAddr)
|
||||
providerSchema := d.Evaluator.ProviderSchemas[providerAddr.ProviderConfig.Type]
|
||||
if providerSchema == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var schema *configschema.Block
|
||||
providerType := providerAddr.ProviderConfig.Type
|
||||
typeName := addr.Type
|
||||
schemas := d.Evaluator.Schemas
|
||||
switch addr.Mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
schema = providerSchema.ResourceTypes[addr.Type]
|
||||
return schemas.ResourceTypeConfig(providerType, typeName)
|
||||
case addrs.DataResourceMode:
|
||||
schema = providerSchema.DataSources[addr.Type]
|
||||
return schemas.DataSourceConfig(providerType, typeName)
|
||||
default:
|
||||
log.Printf("[WARN] Don't know how to fetch schema for resource %s", providerAddr)
|
||||
return nil
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
func (d *evaluationStateData) GetTerraformAttr(addr addrs.TerraformAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
|
|
|
@ -65,8 +65,7 @@ func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext {
|
|||
Operation: w.Operation,
|
||||
State: w.Context.state,
|
||||
StateLock: &w.Context.stateLock,
|
||||
ProviderSchemas: w.providerSchemas,
|
||||
ProvidersLock: &w.providerLock,
|
||||
Schemas: w.Context.schemas,
|
||||
VariableValues: w.variableValues,
|
||||
VariableValuesLock: &w.variableValuesLock,
|
||||
}
|
||||
|
@ -77,9 +76,9 @@ func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext {
|
|||
Hooks: w.Context.hooks,
|
||||
InputValue: w.Context.uiInput,
|
||||
Components: w.Context.components,
|
||||
Schemas: w.Context.schemas,
|
||||
ProviderCache: w.providerCache,
|
||||
ProviderInputConfig: w.Context.providerInputConfig,
|
||||
ProviderSchemas: w.providerSchemas,
|
||||
ProviderLock: &w.providerLock,
|
||||
ProvisionerCache: w.provisionerCache,
|
||||
ProvisionerLock: &w.provisionerLock,
|
||||
|
|
|
@ -17,11 +17,23 @@ type Schemas struct {
|
|||
provisioners map[string]*configschema.Block
|
||||
}
|
||||
|
||||
// ProviderSchema returns the entire ProviderSchema object that was produced
|
||||
// by the plugin for the given provider, or nil if no such schema is available.
|
||||
//
|
||||
// It's usually better to go use the more precise methods offered by type
|
||||
// Schemas to handle this detail automatically.
|
||||
func (ss *Schemas) ProviderSchema(typeName string) *ProviderSchema {
|
||||
if ss.providers == nil {
|
||||
return nil
|
||||
}
|
||||
return ss.providers[typeName]
|
||||
}
|
||||
|
||||
// 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(typeName string) *configschema.Block {
|
||||
ps, exists := ss.providers[typeName]
|
||||
if !exists {
|
||||
ps := ss.ProviderSchema(typeName)
|
||||
if ps == nil {
|
||||
return nil
|
||||
}
|
||||
return ps.Provider
|
||||
|
@ -37,12 +49,8 @@ func (ss *Schemas) ProviderConfig(typeName string) *configschema.Block {
|
|||
// always pass the correct provider name, even though it many cases it feels
|
||||
// redundant.
|
||||
func (ss *Schemas) ResourceTypeConfig(providerType string, resourceType string) *configschema.Block {
|
||||
ps, exists := ss.providers[providerType]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ps.ResourceTypes == nil {
|
||||
ps := ss.ProviderSchema(providerType)
|
||||
if ps == nil || ps.ResourceTypes == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -59,12 +67,8 @@ func (ss *Schemas) ResourceTypeConfig(providerType string, resourceType string)
|
|||
// always pass the correct provider name, even though it many cases it feels
|
||||
// redundant.
|
||||
func (ss *Schemas) DataSourceConfig(providerType string, dataSource string) *configschema.Block {
|
||||
ps, exists := ss.providers[providerType]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ps.DataSources == nil {
|
||||
ps := ss.ProviderSchema(providerType)
|
||||
if ps == nil || ps.DataSources == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -197,6 +201,7 @@ func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Con
|
|||
// There's a check deeper in Terraform that makes this a
|
||||
// failure when an empty/invalid provider string is present
|
||||
// in practice.
|
||||
log.Printf("[WARN] LoadSchemas: Resource %s in %s has invalid provider address %q in its state", rsKey, moduleAddrStr, providerAddrStr)
|
||||
diags = diags.Append(
|
||||
tfdiags.SimpleWarning(fmt.Sprintf("Resource %s in %s has invalid provider address %q in its state", rsKey, moduleAddrStr, providerAddrStr)),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue