mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
71d7a9e480
Having a missing schema is a programming error, but at the time of this commit we're in the midst of introducing schema all over Terraform and so there are inevitably some places -- particularly in older unit tests -- where schema isn't yet being provided. This error allows us to catch those cases and fail gracefully, rather than panicking further down here when we access t.Components methods. Also includes some additional logging to aid debugging of this transformer.
153 lines
5.3 KiB
Go
153 lines
5.3 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/dag"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
|
|
"github.com/hashicorp/terraform/config/configschema"
|
|
)
|
|
|
|
// GraphNodeAttachResourceSchema is an interface implemented by node types
|
|
// that need a resource schema attached.
|
|
type GraphNodeAttachResourceSchema interface {
|
|
GraphNodeResource
|
|
GraphNodeProviderConsumer
|
|
|
|
AttachResourceSchema(*configschema.Block)
|
|
}
|
|
|
|
// GraphNodeAttachProviderConfigSchema is an interface implemented by node types
|
|
// that need a provider configuration schema attached.
|
|
type GraphNodeAttachProviderConfigSchema interface {
|
|
GraphNodeProvider
|
|
|
|
AttachProviderConfigSchema(*configschema.Block)
|
|
}
|
|
|
|
// AttachSchemaTransformer finds nodes that implement either
|
|
// GraphNodeAttachResourceSchema or GraphNodeAttachProviderConfigSchema, looks up
|
|
// the schema for each, and then passes it to a method implemented by the
|
|
// node.
|
|
type AttachSchemaTransformer struct {
|
|
Components contextComponentFactory
|
|
}
|
|
|
|
func (t *AttachSchemaTransformer) Transform(g *Graph) error {
|
|
|
|
if t.Components == nil {
|
|
// Should never happen with a reasonable caller, but we'll return a
|
|
// proper error here anyway so that we'll fail gracefully.
|
|
return fmt.Errorf("AttachSchemaTransformer used with nil Components")
|
|
}
|
|
|
|
// 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() {
|
|
switch tv := v.(type) {
|
|
case GraphNodeAttachResourceSchema:
|
|
addr := tv.ResourceAddr()
|
|
mode := addr.Resource.Mode
|
|
typeName := addr.Resource.Type
|
|
providerAddr, _ := tv.ProvidedBy()
|
|
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 {
|
|
case addrs.ManagedResourceMode:
|
|
schema = providerSchema.ResourceTypes[typeName]
|
|
case addrs.DataResourceMode:
|
|
schema = providerSchema.DataSources[typeName]
|
|
}
|
|
if schema != nil {
|
|
log.Printf("[TRACE] AttachSchemaTransformer: attaching schema to %s", dag.VertexName(v))
|
|
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
|
|
}
|
|
|
|
schema := schemas[providerAddr.ProviderConfig.Type].Provider
|
|
|
|
if schema != nil {
|
|
log.Printf("[TRACE] AttachSchemaTransformer: attaching schema to %s", dag.VertexName(v))
|
|
tv.AttachProviderConfigSchema(schema)
|
|
} else {
|
|
log.Printf("[ERROR] AttachSchemaTransformer: No schema available for %s", providerAddr)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|