core: Fetch schemas during context construction

Previously we fetched schemas during the AttachSchemaTransformer,
potentially multiple times as that was re-run for each graph built. Now
we fetch the schemas just once during context construction, passing that
result into each of the graph builders.

This only addresses the schema accesses during graph construction. We're
still separately loading schemas during the main walk for evaluation
purposes. This will be addressed in a later commit.
This commit is contained in:
Martin Atkins 2018-05-31 12:39:45 -07:00
parent c036613ed3
commit 88b5607a7a
21 changed files with 466 additions and 285 deletions

View File

@ -96,6 +96,7 @@ type Context struct {
// fail regardless but putting this note here as well. // fail regardless but putting this note here as well.
components contextComponentFactory components contextComponentFactory
schemas *Schemas
destroy bool destroy bool
diff *Diff diff *Diff
diffLock sync.RWMutex diffLock sync.RWMutex
@ -203,26 +204,35 @@ func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) {
providers = make(map[string]ResourceProviderFactory) providers = make(map[string]ResourceProviderFactory)
} }
components := &basicComponentFactory{
providers: providers,
provisioners: opts.Provisioners,
}
schemas, err := LoadSchemas(opts.Config, opts.State, components)
if err != nil {
diags = diags.Append(err)
return nil, diags
}
diff := opts.Diff diff := opts.Diff
if diff == nil { if diff == nil {
diff = &Diff{} diff = &Diff{}
} }
return &Context{ return &Context{
components: &basicComponentFactory{ components: components,
providers: providers, schemas: schemas,
provisioners: opts.Provisioners, destroy: opts.Destroy,
}, diff: diff,
destroy: opts.Destroy, hooks: hooks,
diff: diff, meta: opts.Meta,
hooks: hooks, config: opts.Config,
meta: opts.Meta, shadow: opts.Shadow,
config: opts.Config, state: state,
shadow: opts.Shadow, targets: opts.Targets,
state: state, uiInput: opts.UIInput,
targets: opts.Targets, variables: variables,
uiInput: opts.UIInput,
variables: variables,
parallelSem: NewSemaphore(par), parallelSem: NewSemaphore(par),
providerInputConfig: make(map[string]map[string]cty.Value), providerInputConfig: make(map[string]map[string]cty.Value),
@ -255,6 +265,7 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags.
Diff: c.diff, Diff: c.diff,
State: c.state, State: c.state,
Components: c.components, Components: c.components,
Schemas: c.schemas,
Targets: c.targets, Targets: c.targets,
Destroy: c.destroy, Destroy: c.destroy,
Validate: opts.Validate, Validate: opts.Validate,
@ -272,6 +283,7 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags.
Config: c.config, Config: c.config,
State: c.state, State: c.state,
Components: c.components, Components: c.components,
Schemas: c.schemas,
Targets: c.targets, Targets: c.targets,
Validate: opts.Validate, Validate: opts.Validate,
} }
@ -289,11 +301,11 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags.
case GraphTypePlanDestroy: case GraphTypePlanDestroy:
return (&DestroyPlanGraphBuilder{ return (&DestroyPlanGraphBuilder{
Config: c.config, Config: c.config,
State: c.state, State: c.state,
Components: c.components, Schemas: c.schemas,
Targets: c.targets, Targets: c.targets,
Validate: opts.Validate, Validate: opts.Validate,
}).Build(addrs.RootModuleInstance) }).Build(addrs.RootModuleInstance)
case GraphTypeRefresh: case GraphTypeRefresh:
@ -301,6 +313,7 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags.
Config: c.config, Config: c.config,
State: c.state, State: c.state,
Components: c.components, Components: c.components,
Schemas: c.schemas,
Targets: c.targets, Targets: c.targets,
Validate: opts.Validate, Validate: opts.Validate,
}).Build(addrs.RootModuleInstance) }).Build(addrs.RootModuleInstance)
@ -310,6 +323,7 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags.
Config: c.config, Config: c.config,
State: c.state, State: c.state,
Components: c.components, Components: c.components,
Schemas: c.schemas,
}).Build(addrs.RootModuleInstance) }).Build(addrs.RootModuleInstance)
default: default:

View File

@ -61,6 +61,7 @@ func (c *Context) Import(opts *ImportOpts) (*State, tfdiags.Diagnostics) {
ImportTargets: opts.Targets, ImportTargets: opts.Targets,
Config: config, Config: config,
Components: c.components, Components: c.components,
Schemas: c.schemas,
} }
// Build the graph! // Build the graph!

View File

@ -1220,6 +1220,7 @@ func TestContext2Validate_PlanGraphBuilder(t *testing.T) {
Config: c.config, Config: c.config,
State: NewState(), State: NewState(),
Components: c.components, Components: c.components,
Schemas: c.schemas,
Targets: c.targets, Targets: c.targets,
}).Build(addrs.RootModuleInstance) }).Build(addrs.RootModuleInstance)
if diags.HasErrors() { if diags.HasErrors() {

View File

@ -28,6 +28,10 @@ type ApplyGraphBuilder struct {
// provisioners) available for use. // provisioners) available for use.
Components contextComponentFactory Components contextComponentFactory
// Schemas is the repository of schemas we will draw from to analyse
// the configuration.
Schemas *Schemas
// Targets are resources to target. This is only required to make sure // Targets are resources to target. This is only required to make sure
// unnecessary outputs aren't included in the apply graph. The plan // unnecessary outputs aren't included in the apply graph. The plan
// builder successfully handles targeting resources. In the future, // builder successfully handles targeting resources. In the future,
@ -86,16 +90,16 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
// Destruction ordering // Destruction ordering
&DestroyEdgeTransformer{ &DestroyEdgeTransformer{
Config: b.Config, Config: b.Config,
State: b.State, State: b.State,
Components: b.Components, Schemas: b.Schemas,
}, },
GraphTransformIf( GraphTransformIf(
func() bool { return !b.Destroy }, func() bool { return !b.Destroy },
&CBDEdgeTransformer{ &CBDEdgeTransformer{
Config: b.Config, Config: b.Config,
State: b.State, State: b.State,
Components: b.Components, Schemas: b.Schemas,
}, },
), ),
@ -117,7 +121,7 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
// Must be before TransformProviders and ReferenceTransformer, since // Must be before TransformProviders and ReferenceTransformer, since
// schema is required to extract references from config. // schema is required to extract references from config.
&AttachSchemaTransformer{Components: b.Components}, &AttachSchemaTransformer{Schemas: b.Schemas},
// add providers // add providers
TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config),

View File

@ -69,6 +69,7 @@ func TestApplyGraphBuilder(t *testing.T) {
Config: testModule(t, "graph-builder-apply-basic"), Config: testModule(t, "graph-builder-apply-basic"),
Diff: diff, Diff: diff,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true, DisableReduce: true,
} }
@ -122,6 +123,7 @@ func TestApplyGraphBuilder_depCbd(t *testing.T) {
Config: testModule(t, "graph-builder-apply-dep-cbd"), Config: testModule(t, "graph-builder-apply-dep-cbd"),
Diff: diff, Diff: diff,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true, DisableReduce: true,
} }
@ -187,6 +189,7 @@ func TestApplyGraphBuilder_doubleCBD(t *testing.T) {
Config: testModule(t, "graph-builder-apply-double-cbd"), Config: testModule(t, "graph-builder-apply-double-cbd"),
Diff: diff, Diff: diff,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true, DisableReduce: true,
} }
@ -257,6 +260,7 @@ func TestApplyGraphBuilder_destroyStateOnly(t *testing.T) {
Diff: diff, Diff: diff,
State: state, State: state,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true, DisableReduce: true,
} }
@ -304,6 +308,7 @@ func TestApplyGraphBuilder_destroyCount(t *testing.T) {
Config: testModule(t, "graph-builder-apply-count"), Config: testModule(t, "graph-builder-apply-count"),
Diff: diff, Diff: diff,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true, DisableReduce: true,
} }
@ -350,6 +355,7 @@ func TestApplyGraphBuilder_moduleDestroy(t *testing.T) {
Config: testModule(t, "graph-builder-apply-module-destroy"), Config: testModule(t, "graph-builder-apply-module-destroy"),
Diff: diff, Diff: diff,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
} }
g, err := b.Build(addrs.RootModuleInstance) g, err := b.Build(addrs.RootModuleInstance)
@ -387,6 +393,7 @@ func TestApplyGraphBuilder_provisioner(t *testing.T) {
Config: testModule(t, "graph-builder-apply-provisioner"), Config: testModule(t, "graph-builder-apply-provisioner"),
Diff: diff, Diff: diff,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
} }
g, err := b.Build(addrs.RootModuleInstance) g, err := b.Build(addrs.RootModuleInstance)
@ -421,6 +428,7 @@ func TestApplyGraphBuilder_provisionerDestroy(t *testing.T) {
Config: testModule(t, "graph-builder-apply-provisioner"), Config: testModule(t, "graph-builder-apply-provisioner"),
Diff: diff, Diff: diff,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
} }
g, err := b.Build(addrs.RootModuleInstance) g, err := b.Build(addrs.RootModuleInstance)
@ -472,6 +480,7 @@ func TestApplyGraphBuilder_targetModule(t *testing.T) {
Config: testModule(t, "graph-builder-apply-target-module"), Config: testModule(t, "graph-builder-apply-target-module"),
Diff: diff, Diff: diff,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
Targets: []addrs.Targetable{ Targets: []addrs.Targetable{
addrs.RootModuleInstance.Child("child2", addrs.NoKey), addrs.RootModuleInstance.Child("child2", addrs.NoKey),
}, },

View File

@ -22,9 +22,9 @@ type DestroyPlanGraphBuilder struct {
// Targets are resources to target // Targets are resources to target
Targets []addrs.Targetable Targets []addrs.Targetable
// Components is a factory for the plug-in components (providers and // Schemas is the repository of schemas we will draw from to analyse
// provisioners) available for use. // the configuration.
Components contextComponentFactory Schemas *Schemas
// Validate will do structural validation of the graph. // Validate will do structural validation of the graph.
Validate bool Validate bool
@ -60,9 +60,9 @@ func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer {
// Destruction ordering. We require this only so that // Destruction ordering. We require this only so that
// targeting below will prune the correct things. // targeting below will prune the correct things.
&DestroyEdgeTransformer{ &DestroyEdgeTransformer{
Config: b.Config, Config: b.Config,
State: b.State, State: b.State,
Components: b.Components, Schemas: b.Schemas,
}, },
// Target. Note we don't set "Destroy: true" here since we already // Target. Note we don't set "Destroy: true" here since we already

View File

@ -1,11 +1,10 @@
package terraform package terraform
import ( import (
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/tfdiags"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/tfdiags"
) )
// EvalGraphBuilder implements GraphBuilder and constructs a graph suitable // EvalGraphBuilder implements GraphBuilder and constructs a graph suitable
@ -33,6 +32,10 @@ type EvalGraphBuilder struct {
// Components is a factory for the plug-in components (providers and // Components is a factory for the plug-in components (providers and
// provisioners) available for use. // provisioners) available for use.
Components contextComponentFactory Components contextComponentFactory
// Schemas is the repository of schemas we will draw from to analyse
// the configuration.
Schemas *Schemas
} }
// See GraphBuilder // See GraphBuilder
@ -72,7 +75,7 @@ func (b *EvalGraphBuilder) Steps() []GraphTransformer {
// Must be before TransformProviders and ReferenceTransformer, since // Must be before TransformProviders and ReferenceTransformer, since
// schema is required to extract references from config. // schema is required to extract references from config.
&AttachSchemaTransformer{Components: b.Components}, &AttachSchemaTransformer{Schemas: b.Schemas},
TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config),

View File

@ -19,6 +19,10 @@ type ImportGraphBuilder struct {
// Components is the factory for our available plugin components. // Components is the factory for our available plugin components.
Components contextComponentFactory Components contextComponentFactory
// Schemas is the repository of schemas we will draw from to analyse
// the configuration.
Schemas *Schemas
} }
// Build builds the graph according to the steps returned by Steps. // Build builds the graph according to the steps returned by Steps.
@ -59,7 +63,7 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer {
// Must be before TransformProviders and ReferenceTransformer, since // Must be before TransformProviders and ReferenceTransformer, since
// schema is required to extract references from config. // schema is required to extract references from config.
&AttachSchemaTransformer{Components: b.Components}, &AttachSchemaTransformer{Schemas: b.Schemas},
TransformProviders(b.Components.ResourceProviders(), concreteProvider, config), TransformProviders(b.Components.ResourceProviders(), concreteProvider, config),

View File

@ -33,6 +33,10 @@ type PlanGraphBuilder struct {
// provisioners) available for use. // provisioners) available for use.
Components contextComponentFactory Components contextComponentFactory
// Schemas is the repository of schemas we will draw from to analyse
// the configuration.
Schemas *Schemas
// Targets are resources to target // Targets are resources to target
Targets []addrs.Targetable Targets []addrs.Targetable
@ -103,7 +107,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
&MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()},
&AttachSchemaTransformer{Components: b.Components}, &AttachSchemaTransformer{Schemas: b.Schemas},
// Add module variables // Add module variables
&ModuleVariableTransformer{ &ModuleVariableTransformer{
@ -117,7 +121,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
// Must be before ReferenceTransformer, since schema is required to // Must be before ReferenceTransformer, since schema is required to
// extract references from config. // extract references from config.
&AttachSchemaTransformer{Components: b.Components}, &AttachSchemaTransformer{Schemas: b.Schemas},
// Connect so that the references are ready for targeting. We'll // Connect so that the references are ready for targeting. We'll
// have to connect again later for providers and so on. // have to connect again later for providers and so on.
@ -169,7 +173,6 @@ func (b *PlanGraphBuilder) init() {
b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex { b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
return &NodePlannableResource{ return &NodePlannableResource{
NodeAbstractResource: a, NodeAbstractResource: a,
Components: b.Components,
} }
} }

View File

@ -13,28 +13,38 @@ func TestPlanGraphBuilder_impl(t *testing.T) {
} }
func TestPlanGraphBuilder(t *testing.T) { func TestPlanGraphBuilder(t *testing.T) {
awsProvider := &MockResourceProvider{
GetSchemaReturn: &ProviderSchema{
Provider: simpleTestSchema(),
ResourceTypes: map[string]*configschema.Block{
"aws_security_group": simpleTestSchema(),
"aws_instance": simpleTestSchema(),
"aws_load_balancer": simpleTestSchema(),
},
},
}
openstackProvider := &MockResourceProvider{
GetSchemaReturn: &ProviderSchema{
Provider: simpleTestSchema(),
ResourceTypes: map[string]*configschema.Block{
"openstack_floating_ip": simpleTestSchema(),
},
},
}
components := &basicComponentFactory{
providers: map[string]ResourceProviderFactory{
"aws": ResourceProviderFactoryFixed(awsProvider),
"openstack": ResourceProviderFactoryFixed(openstackProvider),
},
}
b := &PlanGraphBuilder{ b := &PlanGraphBuilder{
Config: testModule(t, "graph-builder-plan-basic"), Config: testModule(t, "graph-builder-plan-basic"),
Components: &basicComponentFactory{ Components: components,
providers: map[string]ResourceProviderFactory{ Schemas: &Schemas{
"aws": ResourceProviderFactoryFixed(&MockResourceProvider{ providers: map[string]*ProviderSchema{
GetSchemaReturn: &ProviderSchema{ "aws": awsProvider.GetSchemaReturn,
Provider: simpleTestSchema(), "openstack": openstackProvider.GetSchemaReturn,
ResourceTypes: map[string]*configschema.Block{
"aws_security_group": simpleTestSchema(),
"aws_instance": simpleTestSchema(),
"aws_load_balancer": simpleTestSchema(),
},
},
}),
"openstack": ResourceProviderFactoryFixed(&MockResourceProvider{
GetSchemaReturn: &ProviderSchema{
Provider: simpleTestSchema(),
ResourceTypes: map[string]*configschema.Block{
"openstack_floating_ip": simpleTestSchema(),
},
},
}),
}, },
}, },
DisableReduce: true, DisableReduce: true,
@ -60,6 +70,7 @@ func TestPlanGraphBuilder_targetModule(t *testing.T) {
b := &PlanGraphBuilder{ b := &PlanGraphBuilder{
Config: testModule(t, "graph-builder-plan-target-module-provider"), Config: testModule(t, "graph-builder-plan-target-module-provider"),
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
Targets: []addrs.Targetable{ Targets: []addrs.Targetable{
addrs.RootModuleInstance.Child("child2", addrs.NoKey), addrs.RootModuleInstance.Child("child2", addrs.NoKey),
}, },

View File

@ -33,6 +33,10 @@ type RefreshGraphBuilder struct {
// provisioners) available for use. // provisioners) available for use.
Components contextComponentFactory Components contextComponentFactory
// Schemas is the repository of schemas we will draw from to analyse
// the configuration.
Schemas *Schemas
// Targets are resources to target // Targets are resources to target
Targets []addrs.Targetable Targets []addrs.Targetable
@ -136,7 +140,7 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
// Must be before TransformProviders and ReferenceTransformer, since // Must be before TransformProviders and ReferenceTransformer, since
// schema is required to extract references from config. // schema is required to extract references from config.
&AttachSchemaTransformer{Components: b.Components}, &AttachSchemaTransformer{Schemas: b.Schemas},
TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config),

View File

@ -73,6 +73,7 @@ func TestRefreshGraphBuilder_configOrphans(t *testing.T) {
Config: m, Config: m,
State: state, State: state,
Components: simpleMockComponentFactory(), Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
} }
g, err := b.Build(addrs.RootModuleInstance) g, err := b.Build(addrs.RootModuleInstance)
if err != nil { if err != nil {

View File

@ -9,10 +9,6 @@ import (
// it is ready to be planned in order to create a diff. // it is ready to be planned in order to create a diff.
type NodePlannableResource struct { type NodePlannableResource struct {
*NodeAbstractResource *NodeAbstractResource
// Components is the component factory to use when performing
// DynamicExpand, to access plugins necessary to build the subgraph.
Components contextComponentFactory
} }
var ( var (
@ -48,6 +44,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
// Add the config and state since we don't do that via transforms // Add the config and state since we don't do that via transforms
a.Config = n.Config a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider a.ResolvedProvider = n.ResolvedProvider
a.Schema = n.Schema
return &NodePlannableResourceInstance{ return &NodePlannableResourceInstance{
NodeAbstractResourceInstance: a, NodeAbstractResourceInstance: a,
@ -59,6 +56,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
// Add the config and state since we don't do that via transforms // Add the config and state since we don't do that via transforms
a.Config = n.Config a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider a.ResolvedProvider = n.ResolvedProvider
a.Schema = n.Schema
return &NodePlannableResourceInstanceOrphan{ return &NodePlannableResourceInstanceOrphan{
NodeAbstractResourceInstance: a, NodeAbstractResourceInstance: a,
@ -89,12 +87,6 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
// Targeting // Targeting
&TargetsTransformer{Targets: n.Targets}, &TargetsTransformer{Targets: n.Targets},
// Schemas must be attached before ReferenceTransformer, so we can
// properly analyze the configuration.
&AttachSchemaTransformer{
Components: n.Components,
},
// Connect references so ordering is correct // Connect references so ordering is correct
&ReferenceTransformer{}, &ReferenceTransformer{},

View File

@ -1,18 +1,264 @@
package terraform package terraform
import ( import (
"fmt"
"log"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/config/configschema" "github.com/hashicorp/terraform/config/configschema"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/tfdiags"
) )
// Schemas is a container for various kinds of schema that Terraform needs
// during processing.
type Schemas struct { type Schemas struct {
Providers ProviderSchemas providers map[string]*ProviderSchema
provisioners map[string]*configschema.Block
} }
// ProviderSchemas is a map from provider names to provider schemas. // 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 {
return nil
}
return ps.Provider
}
// ResourceTypeConfig returns the schema for the configuration of a given
// resource type belonging to a given provider type, or nil of no such
// schema is available.
// //
// The names in this map are the direct plugin name (e.g. "aws") rather than // In many cases the provider type is inferrable from the resource type name,
// any alias name (e.g. "aws.foo"), since. // but this is not always true because users can override the provider for
type ProviderSchemas map[string]*ProviderSchema // a resource using the "provider" meta-argument. Therefore it's important to
// 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 {
return nil
}
return ps.ResourceTypes[resourceType]
}
// DataSourceConfig returns the schema for the configuration of a given
// data source belonging to a given provider type, or nil of no such
// schema is available.
//
// In many cases the provider type is inferrable from the data source name,
// but this is not always true because users can override the provider for
// a resource using the "provider" meta-argument. Therefore it's important to
// 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 {
return nil
}
return ps.DataSources[dataSource]
}
// ProvisionerConfig returns the schema for the configuration of a given
// provisioner, or nil of no such schema is available.
func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
return ss.provisioners[name]
}
// LoadSchemas searches the given configuration and state (either of which may
// be nil) for constructs that have an associated schema, requests the
// necessary schemas from the given component factory (which may _not_ be nil),
// and returns a single object representing all of the necessary schemas.
//
// If an error is returned, it may be a wrapped tfdiags.Diagnostics describing
// errors across multiple separate objects. Errors here will usually indicate
// either misbehavior on the part of one of the providers or of the provider
// protocol itself. When returned with errors, the returned schemas object is
// still valid but may be incomplete.
func LoadSchemas(config *configs.Config, state *State, components contextComponentFactory) (*Schemas, error) {
schemas := &Schemas{
providers: map[string]*ProviderSchema{},
provisioners: map[string]*configschema.Block{},
}
var diags tfdiags.Diagnostics
newDiags := loadProviderSchemas(schemas.providers, config, state, components)
diags = diags.Append(newDiags)
newDiags = loadProvisionerSchemas(schemas.provisioners, config, components)
diags = diags.Append(newDiags)
return schemas, diags.Err()
}
func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Config, state *State, components contextComponentFactory) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
ensure := func(typeName string) {
if _, exists := schemas[typeName]; exists {
return
}
log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", typeName)
provider, err := components.ResourceProvider(typeName, "early/"+typeName)
if err != nil {
// We'll put a stub in the map so we won't re-attempt this on
// future calls.
schemas[typeName] = &ProviderSchema{}
diags = diags.Append(
fmt.Errorf("Failed to instantiate provider %q to obtain schema: %s", typeName, err),
)
return
}
defer func() {
if closer, ok := provider.(ResourceProviderCloser); ok {
closer.Close()
}
}()
// FIXME: The provider interface is currently awkward in that it
// requires us to tell the provider which resources types and data
// sources we need. In future this will change to just return
// everything available, but for now we'll fake that by fetching all
// of the available names and then requesting them.
resourceTypes := provider.Resources()
dataSources := provider.DataSources()
resourceTypeNames := make([]string, len(resourceTypes))
for i, o := range resourceTypes {
resourceTypeNames[i] = o.Name
}
dataSourceNames := make([]string, len(dataSources))
for i, o := range dataSources {
dataSourceNames[i] = o.Name
}
schema, err := provider.GetSchema(&ProviderSchemaRequest{
ResourceTypes: resourceTypeNames,
DataSources: dataSourceNames,
})
if err != nil {
// We'll put a stub in the map so we won't re-attempt this on
// future calls.
schemas[typeName] = &ProviderSchema{}
diags = diags.Append(
fmt.Errorf("Failed to retrieve schema from provider %q: %s", typeName, err),
)
return
}
schemas[typeName] = schema
}
if config != nil {
for _, pc := range config.Module.ProviderConfigs {
ensure(pc.Name)
}
for _, rc := range config.Module.ManagedResources {
providerAddr := rc.ProviderConfigAddr()
ensure(providerAddr.Type)
}
for _, rc := range config.Module.DataResources {
providerAddr := rc.ProviderConfigAddr()
ensure(providerAddr.Type)
}
// Must also visit our child modules, recursively.
for _, cc := range config.Children {
childDiags := loadProviderSchemas(schemas, cc, nil, components)
diags = diags.Append(childDiags)
}
}
if state != nil {
for _, ms := range state.Modules {
for rsKey, rs := range ms.Resources {
providerAddrStr := rs.Provider
providerAddr, addrDiags := addrs.ParseAbsProviderConfigStr(providerAddrStr)
if addrDiags.HasErrors() {
// Should happen only if someone has tampered manually with
// the state, since we always write valid provider addrs.
moduleAddrStr := normalizeModulePath(ms.Path).String()
if moduleAddrStr == "" {
moduleAddrStr = "the root module"
}
// For now this is a warning, since there are many existing
// test fixtures that have invalid provider configurations.
// There's a check deeper in Terraform that makes this a
// failure when an empty/invalid provider string is present
// in practice.
diags = diags.Append(
tfdiags.SimpleWarning(fmt.Sprintf("Resource %s in %s has invalid provider address %q in its state", rsKey, moduleAddrStr, providerAddrStr)),
)
continue
}
ensure(providerAddr.ProviderConfig.Type)
}
}
}
return diags
}
func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, components contextComponentFactory) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
ensure := func(name string) {
if _, exists := schemas[name]; exists {
return
}
log.Printf("[TRACE] AttachSchemaTransformer: retrieving schema for provisioner %q", name)
provisioner, err := components.ResourceProvisioner(name, "early/"+name)
if err != nil {
// We'll put a stub in the map so we won't re-attempt this on
// future calls.
schemas[name] = &configschema.Block{}
diags = diags.Append(
fmt.Errorf("Failed to instantiate provisioner %q to obtain schema: %s", name, err),
)
return
}
defer func() {
if closer, ok := provisioner.(ResourceProvisionerCloser); ok {
closer.Close()
}
}()
schema, err := provisioner.GetConfigSchema()
if err != nil {
// We'll put a stub in the map so we won't re-attempt this on
// future calls.
schemas[name] = &configschema.Block{}
diags = diags.Append(
fmt.Errorf("Failed to retrieve schema from provisioner %q: %s", name, err),
)
return
}
schemas[name] = schema
}
if config != nil {
for _, rc := range config.Module.ManagedResources {
for _, pc := range rc.Managed.Provisioners {
ensure(pc.Type)
}
}
}
return diags
}
// ProviderSchema represents the schema for a provider's own configuration // ProviderSchema represents the schema for a provider's own configuration
// and the configuration for some or all of its resources and data sources. // and the configuration for some or all of its resources and data sources.

18
terraform/schemas_test.go Normal file
View File

@ -0,0 +1,18 @@
package terraform
import (
"github.com/hashicorp/terraform/config/configschema"
)
func simpleTestSchemas() *Schemas {
provider := simpleMockProvider()
provisioner := simpleMockProvisioner()
return &Schemas{
providers: map[string]*ProviderSchema{
"test": provider.GetSchemaReturn,
},
provisioners: map[string]*configschema.Block{
"test": provisioner.GetConfigSchemaReturnSchema,
},
}
}

View File

@ -4,11 +4,9 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/config/configschema" "github.com/hashicorp/terraform/config/configschema"
"github.com/hashicorp/terraform/dag"
) )
// GraphNodeAttachResourceSchema is an interface implemented by node types // GraphNodeAttachResourceSchema is an interface implemented by node types
@ -45,91 +43,16 @@ type GraphNodeAttachProvisionerSchema interface {
// GraphNodeAttachProvisionerSchema, looks up the needed schemas for each // GraphNodeAttachProvisionerSchema, looks up the needed schemas for each
// and then passes them to a method implemented by the node. // and then passes them to a method implemented by the node.
type AttachSchemaTransformer struct { type AttachSchemaTransformer struct {
GraphNodeProvisionerConsumer Schemas *Schemas
Components contextComponentFactory
} }
func (t *AttachSchemaTransformer) Transform(g *Graph) error { func (t *AttachSchemaTransformer) Transform(g *Graph) error {
if t.Components == nil { if t.Schemas == nil {
// Should never happen with a reasonable caller, but we'll return a // Should never happen with a reasonable caller, but we'll return a
// proper error here anyway so that we'll fail gracefully. // proper error here anyway so that we'll fail gracefully.
return fmt.Errorf("AttachSchemaTransformer used with nil Components") return fmt.Errorf("AttachSchemaTransformer used with nil Schemas")
} }
err := t.attachProviderSchemas(g)
if err != nil {
return err
}
err = t.attachProvisionerSchemas(g)
if err != nil {
return err
}
return nil
}
func (t *AttachSchemaTransformer) attachProviderSchemas(g *Graph) error {
// First we'll figure out which provider types we need to fetch schemas for.
needProviders := make(map[string]struct{})
for _, v := range g.Vertices() {
switch tv := v.(type) {
case GraphNodeAttachResourceSchema:
providerAddr, _ := tv.ProvidedBy()
log.Printf("[TRACE] AttachSchemaTransformer: %q (%T) needs %s", dag.VertexName(v), v, providerAddr)
needProviders[providerAddr.ProviderConfig.Type] = struct{}{}
case GraphNodeAttachProviderConfigSchema:
providerAddr := tv.ProviderAddr()
log.Printf("[TRACE] AttachSchemaTransformer: %q (%T) needs %s", dag.VertexName(v), v, providerAddr)
needProviders[providerAddr.ProviderConfig.Type] = struct{}{}
}
}
// Now we'll fetch each one. This requires us to temporarily instantiate
// them, though this is not a full bootstrap since we don't yet have
// configuration information; the providers will be re-instantiated and
// properly configured during the graph walk.
schemas := make(map[string]*ProviderSchema)
for typeName := range needProviders {
log.Printf("[TRACE] AttachSchemaTransformer: retrieving schema for provider type %q", typeName)
provider, err := t.Components.ResourceProvider(typeName, "early/"+typeName)
if err != nil {
return fmt.Errorf("failed to instantiate provider %q to obtain schema: %s", typeName, err)
}
// FIXME: The provider interface is currently awkward in that it
// requires us to tell the provider which resources types and data
// sources we need. In future this will change to just return
// everything available, but for now we'll fake that by fetching all
// of the available names and then requesting them.
resourceTypes := provider.Resources()
dataSources := provider.DataSources()
resourceTypeNames := make([]string, len(resourceTypes))
for i, o := range resourceTypes {
resourceTypeNames[i] = o.Name
}
dataSourceNames := make([]string, len(dataSources))
for i, o := range dataSources {
dataSourceNames[i] = o.Name
}
schema, err := provider.GetSchema(&ProviderSchemaRequest{
ResourceTypes: resourceTypeNames,
DataSources: dataSourceNames,
})
if err != nil {
return fmt.Errorf("failed to retrieve schema from provider %q: %s", typeName, err)
}
schemas[typeName] = schema
if closer, ok := provider.(ResourceProviderCloser); ok {
closer.Close()
}
}
// Finally we'll once again visit all of the vertices and attach to
// them the schemas we found for them.
for _, v := range g.Vertices() { for _, v := range g.Vertices() {
switch tv := v.(type) { switch tv := v.(type) {
case GraphNodeAttachResourceSchema: case GraphNodeAttachResourceSchema:
@ -137,98 +60,40 @@ func (t *AttachSchemaTransformer) attachProviderSchemas(g *Graph) error {
mode := addr.Resource.Mode mode := addr.Resource.Mode
typeName := addr.Resource.Type typeName := addr.Resource.Type
providerAddr, _ := tv.ProvidedBy() providerAddr, _ := tv.ProvidedBy()
providerType := providerAddr.ProviderConfig.Type
var schema *configschema.Block var schema *configschema.Block
providerSchema := schemas[providerAddr.ProviderConfig.Type]
if providerSchema == nil {
log.Printf("[ERROR] AttachSchemaTransformer: No schema available for %s because provider schema for %q is missing", addr, providerAddr.ProviderConfig.Type)
continue
}
switch mode { switch mode {
case addrs.ManagedResourceMode: case addrs.ManagedResourceMode:
schema = providerSchema.ResourceTypes[typeName] schema = t.Schemas.ResourceTypeConfig(providerType, typeName)
case addrs.DataResourceMode: case addrs.DataResourceMode:
schema = providerSchema.DataSources[typeName] schema = t.Schemas.DataSourceConfig(providerType, typeName)
} }
if schema != nil { if schema == nil {
log.Printf("[TRACE] AttachSchemaTransformer: attaching schema to %s", dag.VertexName(v)) log.Printf("[ERROR] AttachSchemaTransformer: No resource schema available for %s", addr)
tv.AttachResourceSchema(schema)
} else {
log.Printf("[ERROR] AttachSchemaTransformer: No schema available for %s", addr)
}
case GraphNodeAttachProviderConfigSchema:
providerAddr := tv.ProviderAddr()
providerSchema := schemas[providerAddr.ProviderConfig.Type]
if providerSchema == nil {
log.Printf("[ERROR] AttachSchemaTransformer: No schema available for %s because the whole provider schema is missing", providerAddr)
continue continue
} }
log.Printf("[TRACE] AttachSchemaTransformer: attaching resource schema to %s", dag.VertexName(v))
schema := schemas[providerAddr.ProviderConfig.Type].Provider tv.AttachResourceSchema(schema)
case GraphNodeAttachProviderConfigSchema:
if schema != nil { providerAddr := tv.ProviderAddr()
log.Printf("[TRACE] AttachSchemaTransformer: attaching schema to %s", dag.VertexName(v)) schema := t.Schemas.ProviderConfig(providerAddr.ProviderConfig.Type)
tv.AttachProviderConfigSchema(schema) if schema == nil {
} else {
log.Printf("[ERROR] AttachSchemaTransformer: No schema available for %s", providerAddr) log.Printf("[ERROR] AttachSchemaTransformer: No schema available for %s", providerAddr)
continue
} }
} log.Printf("[TRACE] AttachSchemaTransformer: attaching schema to %s", dag.VertexName(v))
} tv.AttachProviderConfigSchema(schema)
return nil
}
func (t *AttachSchemaTransformer) attachProvisionerSchemas(g *Graph) error {
// First we'll figure out which provisioners we need to fetch schemas for.
needProvisioners := make(map[string]struct{})
for _, v := range g.Vertices() {
switch tv := v.(type) {
case GraphNodeAttachProvisionerSchema:
names := tv.ProvisionedBy()
log.Printf("[TRACE] AttachSchemaTransformer: %q (%T) provisioned by %s", dag.VertexName(v), v, names)
for _, name := range names {
needProvisioners[name] = struct{}{}
}
}
}
// Now we'll fetch each one. This requires us to temporarily instantiate
// them, though this is not a full bootstrap since we don't yet have
// configuration information; the provisioners will be re-instantiated and
// properly configured during the graph walk.
schemas := make(map[string]*configschema.Block)
for name := range needProvisioners {
log.Printf("[TRACE] AttachSchemaTransformer: retrieving schema for provisioner %q", name)
provisioner, err := t.Components.ResourceProvisioner(name, "early/"+name)
if err != nil {
return fmt.Errorf("failed to instantiate provisioner %q to obtain schema: %s", name, err)
}
schema, err := provisioner.GetConfigSchema()
if err != nil {
return fmt.Errorf("failed to retrieve schema from provisioner %q: %s", name, err)
}
schemas[name] = schema
if closer, ok := provisioner.(ResourceProvisionerCloser); ok {
closer.Close()
}
}
// Finally we'll once again visit all of the vertices and attach to
// them the schemas we found for them.
for _, v := range g.Vertices() {
switch tv := v.(type) {
case GraphNodeAttachProvisionerSchema: case GraphNodeAttachProvisionerSchema:
names := tv.ProvisionedBy() names := tv.ProvisionedBy()
for _, name := range names { for _, name := range names {
schema := schemas[name] schema := t.Schemas.ProvisionerConfig(name)
if schema != nil { if schema == nil {
log.Printf("[TRACE] AttachSchemaTransformer: attaching provisioner %q schema to %s", name, dag.VertexName(v))
tv.AttachProvisionerSchema(name, schema)
} else {
log.Printf("[ERROR] AttachSchemaTransformer: No schema available for provisioner %q on %q", name, dag.VertexName(v)) log.Printf("[ERROR] AttachSchemaTransformer: No schema available for provisioner %q on %q", name, dag.VertexName(v))
continue
} }
log.Printf("[TRACE] AttachSchemaTransformer: attaching provisioner %q schema to %s", name, dag.VertexName(v))
tv.AttachProvisionerSchema(name, schema)
} }
} }
} }

View File

@ -41,10 +41,10 @@ type CBDEdgeTransformer struct {
Config *configs.Config Config *configs.Config
State *State State *State
// If configuration is present then Components is required in order to // If configuration is present then Schemas is required in order to
// obtain schema information from providers and provisioners in order // obtain schema information from providers and provisioners so we can
// to properly resolve implicit dependencies. // properly resolve implicit dependencies.
Components contextComponentFactory Schemas *Schemas
} }
func (t *CBDEdgeTransformer) Transform(g *Graph) error { func (t *CBDEdgeTransformer) Transform(g *Graph) error {
@ -178,7 +178,7 @@ func (t *CBDEdgeTransformer) depMap(destroyMap map[string][]dag.Vertex) (map[str
&FlatConfigTransformer{Config: t.Config}, &FlatConfigTransformer{Config: t.Config},
&AttachResourceConfigTransformer{Config: t.Config}, &AttachResourceConfigTransformer{Config: t.Config},
&AttachStateTransformer{State: t.State}, &AttachStateTransformer{State: t.State},
&AttachSchemaTransformer{Components: t.Components}, &AttachSchemaTransformer{Schemas: t.Schemas},
&ReferenceTransformer{}, &ReferenceTransformer{},
}, },
Name: "CBDEdgeTransformer", Name: "CBDEdgeTransformer",

View File

@ -17,8 +17,8 @@ func TestCBDEdgeTransformer(t *testing.T) {
{ {
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -27,8 +27,8 @@ func TestCBDEdgeTransformer(t *testing.T) {
{ {
tf := &CBDEdgeTransformer{ tf := &CBDEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -53,8 +53,8 @@ func TestCBDEdgeTransformer_depNonCBD(t *testing.T) {
{ {
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -63,8 +63,8 @@ func TestCBDEdgeTransformer_depNonCBD(t *testing.T) {
{ {
tf := &CBDEdgeTransformer{ tf := &CBDEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -89,8 +89,8 @@ func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
{ {
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -99,8 +99,8 @@ func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
{ {
tf := &CBDEdgeTransformer{ tf := &CBDEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -135,8 +135,8 @@ func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
{ {
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -145,8 +145,8 @@ func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
{ {
tf := &CBDEdgeTransformer{ tf := &CBDEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)

View File

@ -45,10 +45,10 @@ type DestroyEdgeTransformer struct {
Config *configs.Config Config *configs.Config
State *State State *State
// If configuration is present then Components is required in order to // If configuration is present then Schemas is required in order to
// obtain schema information from providers and provisioners in order // obtain schema information from providers and provisioners in order
// to properly resolve implicit dependencies. // to properly resolve implicit dependencies.
Components contextComponentFactory Schemas *Schemas
} }
func (t *DestroyEdgeTransformer) Transform(g *Graph) error { func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
@ -141,7 +141,7 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
// Must be before ReferenceTransformer, since schema is required to // Must be before ReferenceTransformer, since schema is required to
// extract references from config. // extract references from config.
&AttachSchemaTransformer{Components: t.Components}, &AttachSchemaTransformer{Schemas: t.Schemas},
TransformProviders(nil, providerFn, t.Config), TransformProviders(nil, providerFn, t.Config),

View File

@ -12,8 +12,8 @@ func TestDestroyEdgeTransformer_basic(t *testing.T) {
g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A"})
g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-basic"), Config: testModule(t, "transform-destroy-edge-basic"),
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -32,8 +32,8 @@ func TestDestroyEdgeTransformer_create(t *testing.T) {
g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B"})
g.Add(&graphNodeCreatorTest{AddrString: "test_object.A"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.A"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-basic"), Config: testModule(t, "transform-destroy-edge-basic"),
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -52,8 +52,8 @@ func TestDestroyEdgeTransformer_multi(t *testing.T) {
g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B"})
g.Add(&graphNodeDestroyerTest{AddrString: "test_object.C"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.C"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-multi"), Config: testModule(t, "transform-destroy-edge-multi"),
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -70,8 +70,8 @@ func TestDestroyEdgeTransformer_selfRef(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-self-ref"), Config: testModule(t, "transform-destroy-edge-self-ref"),
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -89,8 +89,8 @@ func TestDestroyEdgeTransformer_module(t *testing.T) {
g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.b"}) g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.b"})
g.Add(&graphNodeDestroyerTest{AddrString: "test_object.a"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.a"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-module"), Config: testModule(t, "transform-destroy-edge-module"),
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -109,8 +109,8 @@ func TestDestroyEdgeTransformer_moduleOnly(t *testing.T) {
g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.b"}) g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.b"})
g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.c"}) g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.c"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-module-only"), Config: testModule(t, "transform-destroy-edge-module-only"),
Components: simpleMockComponentFactory(), Schemas: simpleTestSchemas(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)

View File

@ -30,21 +30,26 @@ func TestTransitiveReductionTransformer(t *testing.T) {
{ {
transform := &AttachSchemaTransformer{ transform := &AttachSchemaTransformer{
Components: testProviderComponentFactory( Schemas: &Schemas{
"aws", providers: map[string]*ProviderSchema{
mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ "aws": {
Attributes: map[string]*configschema.Attribute{ ResourceTypes: map[string]*configschema.Block{
"A": { "aws_instance": &configschema.Block{
Type: cty.String, Attributes: map[string]*configschema.Attribute{
Optional: true, "A": {
}, Type: cty.String,
"B": { Optional: true,
Type: cty.String, },
Optional: true, "B": {
Type: cty.String,
Optional: true,
},
},
},
}, },
}, },
}), },
), },
} }
if err := transform.Transform(&g); err != nil { if err := transform.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)