blob: 509c142a129561eab82422e8fb1b6429b8682420 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package loadschemas
import (
"fmt"
"log"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/schemarepo"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// LoadSchemas loads all of the schemas that might be needed to work with the
// given configuration and state, using the given plugins.
func LoadSchemas(config *configs.Config, state *states.State, plugins *Plugins) (*schemarepo.Schemas, error) {
schemas := &schemarepo.Schemas{
Providers: map[addrs.Provider]providers.ProviderSchema{},
Provisioners: map[string]*configschema.Block{},
}
var diags tfdiags.Diagnostics
newDiags := loadProviderSchemas(schemas.Providers, config, state, plugins)
diags = diags.Append(newDiags)
newDiags = loadProvisionerSchemas(schemas.Provisioners, config, plugins)
diags = diags.Append(newDiags)
return schemas, diags.Err()
}
func loadProviderSchemas(schemas map[addrs.Provider]providers.ProviderSchema, config *configs.Config, state *states.State, plugins *Plugins) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
ensure := func(fqn addrs.Provider) {
name := fqn.String()
if _, exists := schemas[fqn]; exists {
return
}
log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", name)
schema, err := plugins.ProviderSchema(fqn)
if err != nil {
// We'll put a stub in the map so we won't re-attempt this on
// future calls, which would then repeat the same error message
// multiple times.
schemas[fqn] = providers.ProviderSchema{}
diags = diags.Append(
tfdiags.Sourceless(
tfdiags.Error,
"Failed to obtain provider schema",
fmt.Sprintf("Could not load the schema for provider %s: %s.", fqn, err),
),
)
return
}
schemas[fqn] = schema
}
if config != nil {
for _, fqn := range config.ProviderTypes() {
ensure(fqn)
}
}
if state != nil {
needed := providers.AddressedTypesAbs(state.ProviderAddrs())
for _, typeAddr := range needed {
ensure(typeAddr)
}
}
return diags
}
func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, plugins *Plugins) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
ensure := func(name string) {
if _, exists := schemas[name]; exists {
return
}
log.Printf("[TRACE] LoadSchemas: retrieving schema for provisioner %q", name)
schema, err := plugins.ProvisionerSchema(name)
if err != nil {
// We'll put a stub in the map so we won't re-attempt this on
// future calls, which would then repeat the same error message
// multiple times.
schemas[name] = &configschema.Block{}
diags = diags.Append(
tfdiags.Sourceless(
tfdiags.Error,
"Failed to obtain provisioner schema",
fmt.Sprintf("Could not load the schema for 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)
}
}
// Must also visit our child modules, recursively.
for _, cc := range config.Children {
childDiags := loadProvisionerSchemas(schemas, cc, plugins)
diags = diags.Append(childDiags)
}
}
return diags
}