Mildwonkey/ps schema (#24312)

* add Config to AttachSchemaTransformer for providerFqn lookup
* terraform: refactor ProvidedBy() to return nil when provider is not set
in config or state
This commit is contained in:
Kristin Laemmert 2020-03-10 14:43:57 -04:00 committed by GitHub
parent ca26efc5af
commit c7cc0afb80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 109 additions and 88 deletions

View File

@ -190,20 +190,19 @@ func (c *Config) gatherProviderTypes(m map[addrs.Provider]struct{}) {
return
}
// FIXME: These are currently all assuming legacy provider addresses.
// As part of phasing those out we'll need to change this to look up
// the true provider addresses via the local-to-FQN mapping table
// stored inside c.Module.
for _, pc := range c.Module.ProviderConfigs {
m[addrs.NewLegacyProvider(pc.Name)] = struct{}{}
fqn := c.Module.ProviderForLocalConfig(addrs.LocalProviderConfig{LocalName: pc.Name})
m[fqn] = struct{}{}
}
for _, rc := range c.Module.ManagedResources {
providerAddr := rc.ProviderConfigAddr()
m[addrs.NewLegacyProvider(providerAddr.LocalName)] = struct{}{}
fqn := c.Module.ProviderForLocalConfig(providerAddr)
m[fqn] = struct{}{}
}
for _, rc := range c.Module.DataResources {
providerAddr := rc.ProviderConfigAddr()
m[addrs.NewLegacyProvider(providerAddr.LocalName)] = struct{}{}
fqn := c.Module.ProviderForLocalConfig(providerAddr)
m[fqn] = struct{}{}
}
// Must also visit our child modules, recursively.
@ -263,5 +262,8 @@ func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addr
// by checking for the provider in module.ProviderRequirements and falling
// back to addrs.NewLegacyProvider if it is not found.
func (c *Config) ProviderForConfigAddr(addr addrs.LocalProviderConfig) addrs.Provider {
if provider, exists := c.Module.ProviderRequirements[addr.LocalName]; exists {
return provider.Type
}
return c.ResolveAbsProviderAddr(addr, addrs.RootModuleInstance).Provider
}

View File

@ -81,13 +81,7 @@ func TestConfigResolveAbsProviderAddr(t *testing.T) {
got := cfg.ResolveAbsProviderAddr(addr, addrs.RootModuleInstance)
want := addrs.AbsProviderConfig{
Module: addrs.RootModuleInstance,
// FIXME: At the time of writing we're not actually supporting
// the explicit mapping to FQNs because we're still in
// legacy-only mode, so this is temporarily correct. However,
// once we are fully supporting this we should expect to see
// the "registry.terraform.io/foo/test" FQN here, while still
// preserving the "boop" alias.
Provider: addrs.NewLegacyProvider("foo-test"),
Provider: addrs.NewProvider(addrs.DefaultRegistryHost, "foo", "test"),
Alias: "boop",
}
if got, want := got.String(), want.String(); got != want {

View File

@ -178,16 +178,13 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
}
for _, reqd := range file.RequiredProviders {
// As an interim *testing* step, we will accept a source argument
// but assume that the source is a legacy provider. This allows us to
// exercise the provider local names -> fqn logic without changing
// terraform's behavior.
var fqn addrs.Provider
if reqd.Source != "" {
// Fixme: once the rest of the provider source logic is implemented,
// update this to get the addrs.Provider by using
// addrs.ParseProviderSourceString()
// FIXME: capture errors
fqn, _ = addrs.ParseProviderSourceString(reqd.Source)
} else {
fqn = addrs.NewLegacyProvider(reqd.Name)
}
fqn := addrs.NewLegacyProvider(reqd.Name)
if existing, exists := m.ProviderRequirements[reqd.Name]; exists {
if existing.Type != fqn {
panic("provider fqn mismatch")
@ -476,7 +473,7 @@ func (m *Module) LocalNameForProvider(p addrs.Provider) string {
// ProviderForLocalConfig returns the provider FQN for a given LocalProviderConfig
func (m *Module) ProviderForLocalConfig(pc addrs.LocalProviderConfig) addrs.Provider {
if provider, exists := m.ProviderRequirements[pc.String()]; exists {
if provider, exists := m.ProviderRequirements[pc.LocalName]; exists {
return provider.Type
}
return addrs.NewLegacyProvider(pc.LocalName)

View File

@ -13,11 +13,7 @@ func TestNewModule_provider_local_name(t *testing.T) {
t.Fatal(diags.Error())
}
// FIXME: while the provider source is set to "foo/test", terraform
// currently assumes everything is a legacy provider and the localname and
// type match. This test will be updated when provider source is fully
// implemented.
p := addrs.NewLegacyProvider("foo-test")
p := addrs.NewProvider(addrs.DefaultRegistryHost, "foo", "test")
if name, exists := mod.ProviderLocalNames[p]; !exists {
t.Fatal("provider FQN foo/test not found")
} else {

View File

@ -151,7 +151,7 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
// Must attach schemas before ReferenceTransformer so that we can
// analyze the configuration to find references.
&AttachSchemaTransformer{Schemas: b.Schemas},
&AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config},
// Create expansion nodes for all of the module calls. This must
// come after all other transformers that create nodes representing

View File

@ -87,7 +87,7 @@ func (b *EvalGraphBuilder) Steps() []GraphTransformer {
// Must attach schemas before ReferenceTransformer so that we can
// analyze the configuration to find references.
&AttachSchemaTransformer{Schemas: b.Schemas},
&AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config},
// Connect so that the references are ready for targeting. We'll
// have to connect again later for providers and so on.

View File

@ -77,7 +77,7 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer {
// Must attach schemas before ReferenceTransformer so that we can
// analyze the configuration to find references.
&AttachSchemaTransformer{Schemas: b.Schemas},
&AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config},
// Connect so that the references are ready for targeting. We'll
// have to connect again later for providers and so on.

View File

@ -135,7 +135,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
// Must attach schemas before ReferenceTransformer so that we can
// analyze the configuration to find references.
&AttachSchemaTransformer{Schemas: b.Schemas},
&AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config},
// Create expansion nodes for all of the module calls. This must
// come after all other transformers that create nodes representing

View File

@ -160,7 +160,7 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
// Must attach schemas before ReferenceTransformer so that we can
// analyze the configuration to find references.
&AttachSchemaTransformer{Schemas: b.Schemas},
&AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config},
// Create expansion nodes for all of the module calls. This must
// come after all other transformers that create nodes representing

View File

@ -295,12 +295,13 @@ func (n *NodeAbstractResource) ProvidedBy() (addrs.ProviderConfig, bool) {
}, false
}
// Use our type and containing module path to guess a provider configuration address.
defaultFQN := n.Addr.Resource.DefaultProvider()
return addrs.AbsProviderConfig{
Provider: defaultFQN,
Module: n.Addr.Module,
}, false
// No provider configuration found
return nil, false
}
// GraphNodeProviderConsumer
func (n *NodeAbstractResource) ImpliedProvider() addrs.Provider {
return n.Addr.Resource.DefaultProvider()
}
// GraphNodeProviderConsumer
@ -322,12 +323,13 @@ func (n *NodeAbstractResourceInstance) ProvidedBy() (addrs.ProviderConfig, bool)
return n.ResourceState.ProviderConfig, true
}
// Use our type and containing module path to guess a provider configuration address
defaultFQN := n.Addr.Resource.DefaultProvider()
return addrs.AbsProviderConfig{
Provider: defaultFQN,
Module: n.Addr.Module,
}, false
// No provider configuration found
return nil, false
}
// GraphNodeProviderConsumer
func (n *NodeAbstractResourceInstance) ImpliedProvider() addrs.Provider {
return n.Addr.Resource.DefaultProvider()
}
// GraphNodeProvisionerConsumer

View File

@ -5,6 +5,7 @@ import (
"log"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/dag"
)
@ -44,6 +45,7 @@ type GraphNodeAttachProvisionerSchema interface {
// and then passes them to a method implemented by the node.
type AttachSchemaTransformer struct {
Schemas *Schemas
Config *configs.Config
}
func (t *AttachSchemaTransformer) Transform(g *Graph) error {
@ -60,14 +62,27 @@ func (t *AttachSchemaTransformer) Transform(g *Graph) error {
mode := addr.Resource.Mode
typeName := addr.Resource.Type
providerAddr, _ := tv.ProvidedBy()
var providerFqn addrs.Provider
var providerFqn addrs.Provider
switch p := providerAddr.(type) {
case addrs.LocalProviderConfig:
// FIXME: need to look up the providerFQN in the config
if t.Config == nil {
providerFqn = addrs.NewLegacyProvider(p.LocalName)
} else {
modConfig := t.Config.DescendentForInstance(tv.Path())
if modConfig == nil {
providerFqn = addrs.NewLegacyProvider(p.LocalName)
} else {
providerFqn = modConfig.Module.ProviderForLocalConfig(addrs.LocalProviderConfig{LocalName: p.LocalName})
}
}
case addrs.AbsProviderConfig:
providerFqn = p.Provider
case nil:
providerFqn = tv.ImpliedProvider()
default:
// This should never happen; the case statements are meant to be exhaustive
panic(fmt.Sprintf("%s: provider for %s couldn't be determined", dag.VertexName(v), addr))
}
schema, version := t.Schemas.ResourceTypeConfig(providerFqn, mode, typeName)

View File

@ -68,6 +68,16 @@ func (n *graphNodeImportState) ProvidedBy() (addrs.ProviderConfig, bool) {
return n.ProviderAddr, false
}
// GraphNodeProviderConsumer
func (n *graphNodeImportState) ImpliedProvider() addrs.Provider {
// We assume that n.ProviderAddr has been properly populated here.
// It's the responsibility of the code creating a graphNodeImportState
// to populate this, possibly by calling DefaultProviderConfig() on the
// resource address to infer an implied provider from the resource type
// name.
return n.ProviderAddr.Provider
}
// GraphNodeProviderConsumer
func (n *graphNodeImportState) SetProvider(addr addrs.AbsProviderConfig) {
n.ResolvedProvider = addr

View File

@ -21,6 +21,7 @@ func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, c
},
// Add any remaining missing providers
&MissingProviderTransformer{
Config: config,
Providers: providers,
Concrete: concrete,
},
@ -64,10 +65,20 @@ type GraphNodeCloseProvider interface {
type GraphNodeProviderConsumer interface {
GraphNodeSubPath
// ProvidedBy returns the address of the provider configuration the node
// refers to. If the returned "exact" value is true, this address will
// be taken exactly. If "exact" is false, a provider configuration from
// an ancestor module may be selected instead.
// refers to, if available. The following value types may be returned:
//
// * addrs.LocalProviderConfig: the provider was set in the resource config
// * addrs.AbsProviderConfig: the provider configuration was taken from the
// instance state.
// * nil: provider was not set in config or state. It is the caller's
// responsibility to determine the implied default provider (see ImpliedProvider())
ProvidedBy() (addr addrs.ProviderConfig, exact bool)
// ImpliedProvider returns the provider FQN implied by the resource type
// name (for eg the "null" in "null_resource"). This should be used when
// ProvidedBy() returns nil.
ImpliedProvider() (addrs addrs.Provider)
// Set the resolved provider address for this resource.
SetProvider(addrs.AbsProviderConfig)
}
@ -113,37 +124,16 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
providerAddr, exact := pv.ProvidedBy()
var absPc addrs.AbsProviderConfig
var providerFqn addrs.Provider
switch p := providerAddr.(type) {
case addrs.AbsProviderConfig:
// ProvidedBy() returns an AbsProviderConfig when the provider
// configuration is set in state, so we do not need to verify
// the FQN matches.
absPc = p
// ProvidedBy() returns an AbsProviderConfig + exact == true
// when the provider configuration is set in state, so we do not
// need to verify the FQN matches.
if exact {
log.Printf("[TRACE] ProviderTransformer: %s is provided by %s exactly", dag.VertexName(v), absPc)
break
}
// if there is no config at all, the assumed default provider
// must be correct.
if t.Config == nil {
break
}
// If `exact` is false, an AbsProviderConfig indicates that
// ProvidedBy() returned an inferred default FQN. We must check
// if the inferred type name matches a non-default provider
// source in the config.
modConfig := t.Config.DescendentForInstance(pv.Path())
if modConfig != nil {
providerFqn = modConfig.Module.ProviderForLocalConfig(addrs.LocalProviderConfig{
LocalName: p.Provider.Type,
})
// This is only a change to the absPc if
// ProviderForLocalConfig returns a different Provider
absPc.Provider = providerFqn
}
case addrs.LocalProviderConfig:
@ -166,13 +156,15 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
absPc.Module = modPath
absPc.Alias = p.Alias
default:
// This should never happen, the case statements are exhaustive
panic(fmt.Sprintf("%s: provider for %s couldn't be determined", dag.VertexName(v), absPc))
}
if !exact {
case nil:
// No provider found in config or state; fall back to implied default provider.
absPc.Provider = pv.ImpliedProvider()
absPc.Module = pv.Path()
log.Printf("[TRACE] ProviderTransformer: %s is provided by %s or inherited equivalent", dag.VertexName(v), absPc)
default:
// This should never happen; the case statements are meant to be exhaustive
panic(fmt.Sprintf("%s: provider for %s couldn't be determined", dag.VertexName(v), absPc))
}
requested[v][absPc.String()] = ProviderRequest{
@ -328,6 +320,9 @@ type MissingProviderTransformer struct {
// Providers is the list of providers we support.
Providers []string
// MissingProviderTransformer needs the config to rule out _implied_ default providers
Config *configs.Config
// Concrete, if set, overrides how the providers are made.
Concrete ConcreteProviderNodeFunc
}
@ -360,18 +355,28 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error {
// it's safe for us to rely on ProvidedBy here rather than waiting for
// the later proper resolution of provider inheritance done by
// ProviderTransformer.
p, _ := pv.ProvidedBy()
providerAddr, _ := pv.ProvidedBy()
var providerFqn addrs.Provider
switch p.(type) {
switch p := providerAddr.(type) {
case addrs.LocalProviderConfig:
if p.(addrs.LocalProviderConfig).Alias != "" {
if p.Alias != "" {
// We do not create default aliased configurations.
log.Println("[TRACE] MissingProviderTransformer: skipping implication of aliased config", p)
continue
}
providerFqn = addrs.NewLegacyProvider(p.(addrs.LocalProviderConfig).LocalName)
modConfig := t.Config.DescendentForInstance(pv.Path())
if modConfig == nil {
providerFqn = addrs.NewLegacyProvider(p.LocalName)
} else {
providerFqn = modConfig.Module.ProviderForLocalConfig(p)
}
case addrs.AbsProviderConfig:
providerFqn = p.(addrs.AbsProviderConfig).Provider
providerFqn = p.Provider
case nil:
providerFqn = pv.ImpliedProvider()
default:
// This should never happen, the case statements are exhaustive
panic(fmt.Sprintf("%s: provider for %s couldn't be determined", dag.VertexName(v), p))