terraform: use addrs.Provider as map keys for provider schemas (#24002)

This is a stepping-stone PR for the provider source project. In this PR
"legcay-stype" FQNs are created from the provider name string. Future
work involves encoding the FQN directly in the AbsProviderConfig and
removing the calls to addrs.NewLegacyProvider().
This commit is contained in:
Kristin Laemmert 2020-02-03 08:18:04 -05:00 committed by GitHub
parent 3e07ae3ff6
commit 80ab551867
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 87 additions and 63 deletions

View File

@ -262,7 +262,10 @@ func RenderPlan(plan *plans.Plan, state *states.State, schemas *terraform.Schema
if rcs.Action == plans.NoOp {
continue
}
providerSchema := schemas.ProviderSchema(rcs.ProviderAddr.ProviderConfig.LocalName)
// FIXME: update this once the provider fqn is available in the AbsProviderConfig
providerFqn := addrs.NewLegacyProvider(rcs.ProviderAddr.ProviderConfig.LocalName)
providerSchema := schemas.ProviderSchema(providerFqn)
if providerSchema == nil {
// Should never happen
ui.Output(fmt.Sprintf("(schema missing for %s)\n", rcs.ProviderAddr))

View File

@ -139,7 +139,9 @@ func formatStateModule(p blockBodyDiffPrinter, m *states.Module, schemas *terraf
}
var schema *configschema.Block
provider := addrs.NewDefaultLocalProviderConfig(addr.DefaultProvider().LegacyString()).Absolute(m.Addr).ProviderConfig.StringCompact()
// TODO: Get the provider FQN when it is available from the AbsoluteProviderConfig, in state
provider := addr.DefaultProvider()
if _, exists := schemas.Providers[provider]; !exists {
// This should never happen in normal use because we should've
// loaded all of the schemas and checked things prior to this

View File

@ -138,8 +138,8 @@ func testProviderSchema() *terraform.ProviderSchema {
func testSchemas() *terraform.Schemas {
provider := testProvider()
return &terraform.Schemas{
Providers: map[string]*terraform.ProviderSchema{
"test": provider.GetSchemaReturn,
Providers: map[addrs.Provider]*terraform.ProviderSchema{
addrs.NewLegacyProvider("test"): provider.GetSchemaReturn,
},
}
}

View File

@ -139,7 +139,9 @@ func marshalProviderConfigs(
}
for k, pc := range c.Module.ProviderConfigs {
schema := schemas.ProviderConfig(pc.Name)
// FIXME: lookup providerFqn from config
providerFqn := addrs.NewLegacyProvider(pc.Name)
schema := schemas.ProviderConfig(providerFqn)
p := providerConfig{
Name: pc.Name,
Alias: pc.Alias,
@ -301,8 +303,10 @@ func marshalResources(resources map[string]*configs.Resource, schemas *terraform
}
}
// TODO: get actual providerFqn
providerFqn := addrs.NewLegacyProvider(v.ProviderConfigAddr().LocalName)
schema, schemaVer := schemas.ResourceTypeConfig(
v.ProviderConfigAddr().LocalName,
providerFqn,
v.Mode,
v.Type,
)

View File

@ -177,8 +177,10 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
continue
}
// FIXME: update this once the provider fqn is available in the AbsProviderConfig
providerFqn := addrs.NewLegacyProvider(rc.ProviderAddr.ProviderConfig.LocalName)
schema, _ := schemas.ResourceTypeConfig(
rc.ProviderAddr.ProviderConfig.LocalName,
providerFqn,
addr.Resource.Resource.Mode,
addr.Resource.Resource.Type,
)

View File

@ -180,8 +180,10 @@ func marshalPlanResources(changes *plans.Changes, ris []addrs.AbsResourceInstanc
)
}
// FIXME: update this once the provider fqn is available in the AbsProviderConfig
providerFqn := addrs.NewLegacyProvider(r.ProviderAddr.ProviderConfig.LocalName)
schema, schemaVer := schemas.ResourceTypeConfig(
r.ProviderAddr.ProviderConfig.LocalName,
providerFqn,
r.Addr.Resource.Resource.Mode,
resource.Type,
)

View File

@ -290,8 +290,8 @@ func TestMarshalPlanResources(t *testing.T) {
func testSchemas() *terraform.Schemas {
return &terraform.Schemas{
Providers: map[string]*terraform.ProviderSchema{
"test": &terraform.ProviderSchema{
Providers: map[addrs.Provider]*terraform.ProviderSchema{
addrs.NewLegacyProvider("test"): &terraform.ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"test_thing": {
Attributes: map[string]*configschema.Attribute{

View File

@ -35,7 +35,7 @@ func Marshal(s *terraform.Schemas) ([]byte, error) {
providers := newProviders()
for k, v := range s.Providers {
providers.Schemas[k] = marshalProvider(v)
providers.Schemas[k.LegacyString()] = marshalProvider(v)
}
ret, err := json.Marshal(providers)

View File

@ -7,6 +7,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/terraform"
)
@ -117,8 +118,8 @@ func TestMarshalProvider(t *testing.T) {
func testProviders() *terraform.Schemas {
return &terraform.Schemas{
Providers: map[string]*terraform.ProviderSchema{
"test": testProvider(),
Providers: map[addrs.Provider]*terraform.ProviderSchema{
addrs.NewLegacyProvider("test"): testProvider(),
},
}
}

View File

@ -273,8 +273,10 @@ func marshalResources(resources map[string]*states.Resource, schemas *terraform.
current.Index = k
}
// FIXME: lookup providerFqn from state
providerFqn := addrs.NewLegacyProvider(r.ProviderConfig.ProviderConfig.LocalName)
schema, _ := schemas.ResourceTypeConfig(
r.ProviderConfig.ProviderConfig.LocalName,
providerFqn,
r.Addr.Mode,
r.Addr.Type,
)

View File

@ -351,8 +351,8 @@ func TestMarshalResources(t *testing.T) {
func testSchemas() *terraform.Schemas {
return &terraform.Schemas{
Providers: map[string]*terraform.ProviderSchema{
"test": &terraform.ProviderSchema{
Providers: map[addrs.Provider]*terraform.ProviderSchema{
addrs.NewLegacyProvider("test"): &terraform.ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"test_thing": {
Attributes: map[string]*configschema.Attribute{

View File

@ -163,7 +163,7 @@ func (c *Config) DescendentForInstance(path addrs.ModuleInstance) *Config {
return current
}
// ProviderTypes returns the names of each distinct provider type referenced
// ProviderTypes returns the FQNs of each distinct provider type referenced
// in the receiving configuration.
//
// This is a helper for easily determining which provider types are required

View File

@ -96,7 +96,9 @@ func (c *Context) Input(mode InputMode) tfdiags.Diagnostics {
UIInput: c.uiInput,
}
schema := c.schemas.ProviderConfig(pa.LocalName)
// TODO: get provider FQN
providerFqn := addrs.NewLegacyProvider(pa.LocalName)
schema := c.schemas.ProviderConfig(providerFqn)
if schema == nil {
// Could either be an incorrect config or just an incomplete
// mock in tests. We'll let a later pass decide, and just

View File

@ -149,7 +149,7 @@ func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *Pro
// FIXME: Once AbsProviderConfig starts containing an FQN, use that directly
// here instead of addr.ProviderConfig.LocalName.
return ctx.Schemas.ProviderSchema(addr.ProviderConfig.LocalName)
return ctx.Schemas.ProviderSchema(addrs.NewLegacyProvider(addr.ProviderConfig.LocalName))
}
func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error {

View File

@ -781,9 +781,9 @@ func (d *evaluationStateData) getResourceInstancesAll(addr addrs.Resource, rng t
func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAddr addrs.AbsProviderConfig) *configschema.Block {
// FIXME: Once AbsProviderConfig has an addrs.Provider in it, we should
// be looking schemas up using provider FQNs rather than legacy names.
providerType := providerAddr.ProviderConfig.LocalName
providerFqn := addrs.NewLegacyProvider(providerAddr.ProviderConfig.LocalName)
schemas := d.Evaluator.Schemas
schema, _ := schemas.ResourceTypeConfig(providerType, addr.Mode, addr.Type)
schema, _ := schemas.ResourceTypeConfig(providerFqn, addr.Mode, addr.Type)
return schema
}

View File

@ -217,8 +217,8 @@ func (d *evaluationStateData) staticValidateResourceReference(modCfg *configs.Co
// legacy addresses. d.Evaluator.Schemas.ResourceTypeConfig below ought to
// change to take an addrs.Provider, and then that's what we should be
// passing in here.
providerType := cfg.ProviderConfigAddr().LocalName
schema, _ := d.Evaluator.Schemas.ResourceTypeConfig(providerType, addr.Mode, addr.Type)
providerFqn := addrs.NewLegacyProvider(cfg.ProviderConfigAddr().LocalName)
schema, _ := d.Evaluator.Schemas.ResourceTypeConfig(providerFqn, addr.Mode, addr.Type)
if schema == nil {
// Prior validation should've taken care of a resource block with an
@ -227,7 +227,7 @@ func (d *evaluationStateData) staticValidateResourceReference(modCfg *configs.Co
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Invalid resource type`,
Detail: fmt.Sprintf(`A %s resource type %q is not supported by provider %q.`, modeAdjective, addr.Type, providerType),
Detail: fmt.Sprintf(`A %s resource type %q is not supported by provider %q.`, modeAdjective, addr.Type, providerFqn.LegacyString()),
Subject: rng.ToHCL().Ptr(),
})
return diags

View File

@ -6,6 +6,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/lang"
)
@ -55,8 +56,8 @@ For example, to correlate with indices of a referring resource, use:
evaluator := &Evaluator{
Config: cfg,
Schemas: &Schemas{
Providers: map[string]*ProviderSchema{
"aws": {
Providers: map[addrs.Provider]*ProviderSchema{
addrs.NewLegacyProvider("aws"): {
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {},
},

View File

@ -500,7 +500,7 @@ func TestApplyGraphBuilder_targetModule(t *testing.T) {
// that resource is destroyed.
func TestApplyGraphBuilder_updateFromOrphan(t *testing.T) {
schemas := simpleTestSchemas()
instanceSchema := schemas.Providers["test"].ResourceTypes["test_object"]
instanceSchema := schemas.Providers[addrs.NewLegacyProvider("test")].ResourceTypes["test_object"]
bBefore, _ := plans.NewDynamicValue(
cty.ObjectVal(map[string]cty.Value{

View File

@ -44,9 +44,9 @@ func TestPlanGraphBuilder(t *testing.T) {
Config: testModule(t, "graph-builder-plan-basic"),
Components: components,
Schemas: &Schemas{
Providers: map[string]*ProviderSchema{
"aws": awsProvider.GetSchemaReturn,
"openstack": openstackProvider.GetSchemaReturn,
Providers: map[addrs.Provider]*ProviderSchema{
addrs.NewLegacyProvider("aws"): awsProvider.GetSchemaReturn,
addrs.NewLegacyProvider("openstack"): openstackProvider.GetSchemaReturn,
},
},
DisableReduce: true,
@ -101,8 +101,8 @@ func TestPlanGraphBuilder_dynamicBlock(t *testing.T) {
Config: testModule(t, "graph-builder-plan-dynblock"),
Components: components,
Schemas: &Schemas{
Providers: map[string]*ProviderSchema{
"test": provider.GetSchemaReturn,
Providers: map[addrs.Provider]*ProviderSchema{
addrs.NewLegacyProvider("test"): provider.GetSchemaReturn,
},
},
DisableReduce: true,
@ -180,8 +180,8 @@ func TestPlanGraphBuilder_attrAsBlocks(t *testing.T) {
Config: testModule(t, "graph-builder-plan-attr-as-blocks"),
Components: components,
Schemas: &Schemas{
Providers: map[string]*ProviderSchema{
"test": provider.GetSchemaReturn,
Providers: map[addrs.Provider]*ProviderSchema{
addrs.NewLegacyProvider("test"): provider.GetSchemaReturn,
},
},
DisableReduce: true,
@ -267,8 +267,8 @@ func TestPlanGraphBuilder_forEach(t *testing.T) {
Config: testModule(t, "plan-for-each"),
Components: components,
Schemas: &Schemas{
Providers: map[string]*ProviderSchema{
"aws": awsProvider.GetSchemaReturn,
Providers: map[addrs.Provider]*ProviderSchema{
addrs.NewLegacyProvider("aws"): awsProvider.GetSchemaReturn,
},
},
DisableReduce: true,

View File

@ -15,7 +15,7 @@ import (
// Schemas is a container for various kinds of schema that Terraform needs
// during processing.
type Schemas struct {
Providers map[string]*ProviderSchema
Providers map[addrs.Provider]*ProviderSchema
Provisioners map[string]*configschema.Block
}
@ -24,17 +24,17 @@ 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(typeName string) *ProviderSchema {
func (ss *Schemas) ProviderSchema(provider addrs.Provider) *ProviderSchema {
if ss.Providers == nil {
return nil
}
return ss.Providers[typeName]
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(typeName string) *configschema.Block {
ps := ss.ProviderSchema(typeName)
func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block {
ps := ss.ProviderSchema(provider)
if ps == nil {
return nil
}
@ -50,8 +50,8 @@ func (ss *Schemas) ProviderConfig(typeName string) *configschema.Block {
// 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, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) {
ps := ss.ProviderSchema(providerType)
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 {
return nil, 0
}
@ -76,7 +76,7 @@ func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
// still valid but may be incomplete.
func LoadSchemas(config *configs.Config, state *states.State, components contextComponentFactory) (*Schemas, error) {
schemas := &Schemas{
Providers: map[string]*ProviderSchema{},
Providers: map[addrs.Provider]*ProviderSchema{},
Provisioners: map[string]*configschema.Block{},
}
var diags tfdiags.Diagnostics
@ -89,16 +89,14 @@ func LoadSchemas(config *configs.Config, state *states.State, components context
return schemas, diags.Err()
}
func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Config, state *states.State, components contextComponentFactory) tfdiags.Diagnostics {
func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *configs.Config, state *states.State, components contextComponentFactory) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
ensure := func(typeAddr addrs.Provider) {
// FIXME: Once schema lookup is ready to look up by addrs.Provider rather
// than legacy name, we'll use typeAddr directly. For now, we support
// only legacy addresses.
typeName := typeAddr.LegacyString()
ensure := func(fqn addrs.Provider) {
// TODO: LegacyString() will be removed in an upcoming release
typeName := fqn.LegacyString()
if _, exists := schemas[typeName]; exists {
if _, exists := schemas[fqn]; exists {
return
}
@ -107,7 +105,7 @@ func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Con
if err != nil {
// We'll put a stub in the map so we won't re-attempt this on
// future calls.
schemas[typeName] = &ProviderSchema{}
schemas[fqn] = &ProviderSchema{}
diags = diags.Append(
fmt.Errorf("Failed to instantiate provider %q to obtain schema: %s", typeName, err),
)
@ -121,7 +119,7 @@ func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Con
if resp.Diagnostics.HasErrors() {
// We'll put a stub in the map so we won't re-attempt this on
// future calls.
schemas[typeName] = &ProviderSchema{}
schemas[fqn] = &ProviderSchema{}
diags = diags.Append(
fmt.Errorf("Failed to retrieve schema from provider %q: %s", typeName, resp.Diagnostics.Err()),
)
@ -165,12 +163,12 @@ func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Con
}
}
schemas[typeName] = s
schemas[fqn] = s
}
if config != nil {
for _, typeName := range config.ProviderTypes() {
ensure(typeName)
for _, fqn := range config.ProviderTypes() {
ensure(fqn)
}
}

View File

@ -1,6 +1,7 @@
package terraform
import (
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
)
@ -8,8 +9,8 @@ func simpleTestSchemas() *Schemas {
provider := simpleMockProvider()
provisioner := simpleMockProvisioner()
return &Schemas{
Providers: map[string]*ProviderSchema{
"test": provider.GetSchemaReturn,
Providers: map[addrs.Provider]*ProviderSchema{
addrs.NewLegacyProvider("test"): provider.GetSchemaReturn,
},
Provisioners: map[string]*configschema.Block{
"test": provisioner.GetSchemaResponse.Provisioner,

View File

@ -4,6 +4,7 @@ import (
"fmt"
"log"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/dag"
)
@ -59,9 +60,11 @@ func (t *AttachSchemaTransformer) Transform(g *Graph) error {
mode := addr.Resource.Mode
typeName := addr.Resource.Type
providerAddr, _ := tv.ProvidedBy()
providerType := providerAddr.ProviderConfig.LocalName
schema, version := t.Schemas.ResourceTypeConfig(providerType, mode, typeName)
// TODO: get providerFQN directly from AbsProviderConfig
providerFqn := addrs.NewLegacyProvider(providerAddr.ProviderConfig.LocalName)
schema, version := t.Schemas.ResourceTypeConfig(providerFqn, mode, typeName)
if schema == nil {
log.Printf("[ERROR] AttachSchemaTransformer: No resource schema available for %s", addr)
continue
@ -72,7 +75,10 @@ func (t *AttachSchemaTransformer) Transform(g *Graph) error {
if tv, ok := v.(GraphNodeAttachProviderConfigSchema); ok {
providerAddr := tv.ProviderAddr()
schema := t.Schemas.ProviderConfig(providerAddr.ProviderConfig.LocalName)
// TODO: get providerFQN directly from AbsProviderConfig
providerFqn := addrs.NewLegacyProvider(providerAddr.ProviderConfig.LocalName)
schema := t.Schemas.ProviderConfig(providerFqn)
if schema == nil {
log.Printf("[ERROR] AttachSchemaTransformer: No provider config schema available for %s", providerAddr)
continue

View File

@ -31,8 +31,8 @@ func TestTransitiveReductionTransformer(t *testing.T) {
{
transform := &AttachSchemaTransformer{
Schemas: &Schemas{
Providers: map[string]*ProviderSchema{
"aws": {
Providers: map[addrs.Provider]*ProviderSchema{
addrs.NewLegacyProvider("aws"): {
ResourceTypes: map[string]*configschema.Block{
"aws_instance": &configschema.Block{
Attributes: map[string]*configschema.Attribute{