mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
Add POC for preconfigured providers
The idea is quite simple but the commited implementation is definitely not in the final shape. Right now, OpenTofu is handling the providers, from the installation, sub-process mantainance, configuration and cleanup of it. By doing so, this adds a lot of run time. This commit is exploring on how to instruct OpenTofu to work with providers pre-configured and hosted by a different process. Therefore, a providers server has been created and can be found in https://github.com/opentofu/hackathon-providers-server. With this commit, was introduced the support of this new experimental block: terraform { // ... preconfigured_providers { playground = { source = "opentofu/playground" addr = "localhost:50051" protocol_version = 6 } } } Having this configured, OpenTofu will just create a gRPC client to communicate with the indicated addr. The server is running the same protobuf schema and it's supposed to function exactly the same as a provider mantained by OpenTofu itself. One important thing to note, is that for every provider that is registered in OpenTofu as pre-configured, the following calls should not be performed: * ValidateProviderConfig * ConfigureProvider Signed-off-by: Andrei Ciobanu <andrei.ciobanu@opentofu.org>
This commit is contained in:
@@ -587,7 +587,7 @@ func (m *Meta) contextOpts(ctx context.Context) (*tofu.ContextOpts, error) {
|
||||
opts.Provisioners = m.testingOverrides.Provisioners
|
||||
} else {
|
||||
var providerFactories map[addrs.Provider]providers.Factory
|
||||
providerFactories, err = m.providerFactories()
|
||||
providerFactories, err = m.providerFactories(m.loadPreconfiguredConfigs(ctx))
|
||||
opts.Providers = providerFactories
|
||||
opts.Provisioners = m.provisionerFactories()
|
||||
}
|
||||
@@ -965,3 +965,15 @@ func (c *Meta) MaybeGetSchemas(ctx context.Context, state *states.State, config
|
||||
}
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
func (c *Meta) loadPreconfiguredConfigs(ctx context.Context) map[addrs.Provider]*configs.PreconfiguredProvider {
|
||||
var res map[addrs.Provider]*configs.PreconfiguredProvider
|
||||
cfg, _ := c.configLoader.LoadConfig(ctx, ".", configs.StaticModuleCall{}) // TODO load selective
|
||||
if cfg == nil {
|
||||
return res
|
||||
}
|
||||
if cfg.Module.PreconfiguredProviders == nil {
|
||||
return res
|
||||
}
|
||||
return cfg.Module.PreconfiguredProviders.PreconfiguredProviders
|
||||
}
|
||||
|
||||
@@ -126,6 +126,11 @@ func (m *Meta) annotateDependencyLocksWithOverrides(ret *depsfile.Locks) *depsfi
|
||||
log.Printf("[DEBUG] Provider %s is overridden as an \"unmanaged provider\"", addr)
|
||||
ret.SetProviderOverridden(addr)
|
||||
}
|
||||
for addr := range m.loadPreconfiguredConfigs(context.TODO()) {
|
||||
log.Printf("[DEBUG] Provider %s is overridden as a \"preconfigured provider\"", addr)
|
||||
ret.SetProviderOverridden(addr)
|
||||
}
|
||||
|
||||
if m.testingOverrides != nil {
|
||||
for addr := range m.testingOverrides.Providers {
|
||||
log.Printf("[DEBUG] Provider %s is overridden in Meta.testingOverrides", addr)
|
||||
|
||||
@@ -10,11 +10,13 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
terraformProvider "github.com/opentofu/opentofu/internal/builtin/providers/tf"
|
||||
@@ -232,7 +234,7 @@ func (m *Meta) providerDevOverrideRuntimeWarnings() tfdiags.Diagnostics {
|
||||
// package have been modified outside of the installer. If it returns an error,
|
||||
// the returned map may be incomplete or invalid, but will be as complete
|
||||
// as possible given the cause of the error.
|
||||
func (m *Meta) providerFactories() (map[addrs.Provider]providers.Factory, error) {
|
||||
func (m *Meta) providerFactories(preconfiguredProviders map[addrs.Provider]*configs.PreconfiguredProvider) (map[addrs.Provider]providers.Factory, error) {
|
||||
locks, diags := m.lockedDependencies()
|
||||
if diags.HasErrors() {
|
||||
return nil, fmt.Errorf("failed to read dependency lock file: %w", diags.Err())
|
||||
@@ -275,7 +277,7 @@ func (m *Meta) providerFactories() (map[addrs.Provider]providers.Factory, error)
|
||||
devOverrideProviders := m.ProviderDevOverrides
|
||||
unmanagedProviders := m.UnmanagedProviders
|
||||
|
||||
factories := make(map[addrs.Provider]providers.Factory, len(providerLocks)+len(internalFactories)+len(unmanagedProviders))
|
||||
factories := make(map[addrs.Provider]providers.Factory, len(providerLocks)+len(internalFactories)+len(unmanagedProviders)+len(preconfiguredProviders))
|
||||
for name, factory := range internalFactories {
|
||||
factories[addrs.NewBuiltInProvider(name)] = factory
|
||||
}
|
||||
@@ -328,6 +330,16 @@ func (m *Meta) providerFactories() (map[addrs.Provider]providers.Factory, error)
|
||||
for provider, localDir := range devOverrideProviders {
|
||||
factories[provider] = devOverrideProviderFactory(provider, localDir)
|
||||
}
|
||||
|
||||
for provider, providerCfg := range preconfiguredProviders {
|
||||
reattach, err := preconfiguredToReattach(providerCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reattach != nil {
|
||||
factories[provider] = preconfiguredProviderFactory(provider, providerCfg, reattach)
|
||||
}
|
||||
}
|
||||
for provider, reattach := range unmanagedProviders {
|
||||
factories[provider] = unmanagedProviderFactory(provider, reattach)
|
||||
}
|
||||
@@ -381,7 +393,7 @@ func providerFactory(meta *providercache.CachedProvider) providers.Factory {
|
||||
}
|
||||
|
||||
protoVer := client.NegotiatedVersion()
|
||||
p, err := initializeProviderInstance(raw, protoVer, client, meta.Provider)
|
||||
p, err := initializeProviderInstance(raw, protoVer, client, meta.Provider, false)
|
||||
if errors.Is(err, errUnsupportedProtocolVersion) {
|
||||
panic(err)
|
||||
}
|
||||
@@ -392,18 +404,20 @@ func providerFactory(meta *providercache.CachedProvider) providers.Factory {
|
||||
|
||||
// initializeProviderInstance uses the plugin dispensed by the RPC client, and initializes a plugin instance
|
||||
// per the protocol version
|
||||
func initializeProviderInstance(plugin interface{}, protoVer int, pluginClient *plugin.Client, pluginAddr addrs.Provider) (providers.Interface, error) {
|
||||
func initializeProviderInstance(plugin interface{}, protoVer int, pluginClient *plugin.Client, pluginAddr addrs.Provider, preconfigured bool) (providers.Interface, error) {
|
||||
// store the client so that the plugin can kill the child process
|
||||
switch protoVer {
|
||||
case 5:
|
||||
p := plugin.(*tfplugin.GRPCProvider)
|
||||
p.PluginClient = pluginClient
|
||||
p.Addr = pluginAddr
|
||||
p.Preconfigured = preconfigured
|
||||
return p, nil
|
||||
case 6:
|
||||
p := plugin.(*tfplugin6.GRPCProvider)
|
||||
p.PluginClient = pluginClient
|
||||
p.Addr = pluginAddr
|
||||
p.Preconfigured = preconfigured
|
||||
return p, nil
|
||||
default:
|
||||
return nil, errUnsupportedProtocolVersion
|
||||
@@ -476,10 +490,92 @@ func unmanagedProviderFactory(provider addrs.Provider, reattach *plugin.Reattach
|
||||
protoVer = 5
|
||||
}
|
||||
|
||||
return initializeProviderInstance(raw, protoVer, client, provider)
|
||||
return initializeProviderInstance(raw, protoVer, client, provider, false)
|
||||
}
|
||||
}
|
||||
|
||||
func preconfiguredProviderFactory(provider addrs.Provider, preconfiguredProvider *configs.PreconfiguredProvider, reattach *plugin.ReattachConfig) providers.Factory {
|
||||
return func() (providers.Interface, error) {
|
||||
config := &plugin.ClientConfig{
|
||||
HandshakeConfig: tfplugin.Handshake,
|
||||
Logger: logging.NewProviderLogger("unmanaged."),
|
||||
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
|
||||
Managed: false,
|
||||
Reattach: reattach,
|
||||
SyncStdout: logging.PluginOutputMonitor(fmt.Sprintf("%s:stdout", provider)),
|
||||
SyncStderr: logging.PluginOutputMonitor(fmt.Sprintf("%s:stderr", provider)),
|
||||
}
|
||||
|
||||
if preconfiguredProvider.ProtocolVersion == 0 {
|
||||
// As of the 0.15 release, sdk.v2 doesn't include the protocol
|
||||
// version in the ReattachConfig (only recently added to
|
||||
// go-plugin), so client.NegotiatedVersion() always returns 0. We
|
||||
// assume that an unmanaged provider reporting protocol version 0 is
|
||||
// actually using proto v5 for backwards compatibility.
|
||||
if defaultPlugins, ok := tfplugin.VersionedPlugins[5]; ok {
|
||||
config.Plugins = defaultPlugins
|
||||
} else {
|
||||
return nil, errors.New("no supported plugins for protocol 0")
|
||||
}
|
||||
} else if plugins, ok := tfplugin.VersionedPlugins[preconfiguredProvider.ProtocolVersion]; !ok {
|
||||
return nil, fmt.Errorf("no supported plugins for protocol %d", preconfiguredProvider.ProtocolVersion)
|
||||
} else {
|
||||
config.Plugins = plugins
|
||||
}
|
||||
|
||||
client := plugin.NewClient(config)
|
||||
rpcClient, err := client.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
raw, err := rpcClient.Dispense(tfplugin.ProviderPluginName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protoVer := client.NegotiatedVersion()
|
||||
if protoVer == 0 {
|
||||
// As of the 0.15 release, sdk.v2 doesn't include the protocol
|
||||
// version in the ReattachConfig (only recently added to
|
||||
// go-plugin), so client.NegotiatedVersion() always returns 0. We
|
||||
// assume that an unmanaged provider reporting protocol version 0 is
|
||||
// actually using proto v5 for backwards compatibility.
|
||||
protoVer = 5
|
||||
}
|
||||
|
||||
return initializeProviderInstance(raw, protoVer, client, provider, true)
|
||||
}
|
||||
}
|
||||
|
||||
func preconfiguredToReattach(provider *configs.PreconfiguredProvider) (resp *plugin.ReattachConfig, err error) {
|
||||
// TODO
|
||||
//switch c.Addr.Network {
|
||||
//case "unix":
|
||||
// addr, err = net.ResolveUnixAddr("unix", c.Addr.String)
|
||||
// if err != nil {
|
||||
// return unmanagedProviders, fmt.Errorf("Invalid unix socket path %q for %q: %w", c.Addr.String, p, err)
|
||||
// }
|
||||
//case "tcp":
|
||||
// addr, err = net.ResolveTCPAddr("tcp", c.Addr.String)
|
||||
// if err != nil {
|
||||
// return unmanagedProviders, fmt.Errorf("Invalid TCP address %q for %q: %w", c.Addr.String, p, err)
|
||||
// }
|
||||
//default:
|
||||
// return unmanagedProviders, fmt.Errorf("Unknown address type %q for %q", c.Addr.Network, p)
|
||||
//}
|
||||
addr, err := net.ResolveTCPAddr("tcp", provider.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &plugin.ReattachConfig{
|
||||
Protocol: plugin.ProtocolGRPC,
|
||||
ProtocolVersion: provider.ProtocolVersion,
|
||||
Addr: addr,
|
||||
Test: true, // To block opentofu from calling close
|
||||
}, nil
|
||||
}
|
||||
|
||||
// providerFactoryError is a stub providers.Factory that returns an error
|
||||
// when called. It's used to allow providerFactories to still produce a
|
||||
// factory for each available provider in an error case, for situations
|
||||
|
||||
@@ -35,13 +35,14 @@ type Module struct {
|
||||
|
||||
ActiveExperiments experiments.Set
|
||||
|
||||
Backend *Backend
|
||||
CloudConfig *CloudConfig
|
||||
ProviderConfigs map[string]*Provider
|
||||
ProviderRequirements *RequiredProviders
|
||||
ProviderLocalNames map[addrs.Provider]string
|
||||
ProviderMetas map[addrs.Provider]*ProviderMeta
|
||||
Encryption *config.EncryptionConfig
|
||||
Backend *Backend
|
||||
CloudConfig *CloudConfig
|
||||
ProviderConfigs map[string]*Provider
|
||||
ProviderRequirements *RequiredProviders
|
||||
PreconfiguredProviders *PreconfiguredProviders
|
||||
ProviderLocalNames map[addrs.Provider]string
|
||||
ProviderMetas map[addrs.Provider]*ProviderMeta
|
||||
Encryption *config.EncryptionConfig
|
||||
|
||||
Variables map[string]*Variable
|
||||
Locals map[string]*Local
|
||||
@@ -91,12 +92,13 @@ type File struct {
|
||||
|
||||
ActiveExperiments experiments.Set
|
||||
|
||||
Backends []*Backend
|
||||
CloudConfigs []*CloudConfig
|
||||
ProviderConfigs []*Provider
|
||||
ProviderMetas []*ProviderMeta
|
||||
RequiredProviders []*RequiredProviders
|
||||
Encryptions []*config.EncryptionConfig
|
||||
Backends []*Backend
|
||||
CloudConfigs []*CloudConfig
|
||||
ProviderConfigs []*Provider
|
||||
ProviderMetas []*ProviderMeta
|
||||
RequiredProviders []*RequiredProviders
|
||||
PreconfiguredProviders []*PreconfiguredProviders
|
||||
Encryptions []*config.EncryptionConfig
|
||||
|
||||
Variables []*Variable
|
||||
Locals []*Local
|
||||
@@ -203,6 +205,20 @@ func NewModule(primaryFiles, overrideFiles []*File, call StaticModuleCall, sourc
|
||||
mod.ProviderRequirements = r
|
||||
}
|
||||
}
|
||||
for _, file := range primaryFiles {
|
||||
for _, r := range file.PreconfiguredProviders { // TODO andrei this should be allowed only in the root module
|
||||
if mod.PreconfiguredProviders != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Duplicate preconfigured providers configuration",
|
||||
Detail: fmt.Sprintf("A module may have only one preconfigured providers configuration. The preconfigured providers were previously configured at %s.", mod.ProviderRequirements.DeclRange),
|
||||
Subject: &r.DeclRange,
|
||||
})
|
||||
continue
|
||||
}
|
||||
mod.PreconfiguredProviders = r
|
||||
}
|
||||
}
|
||||
|
||||
// If no required_providers block is configured, create a useful empty
|
||||
// state to reduce nil checks elsewhere
|
||||
@@ -220,6 +236,11 @@ func NewModule(primaryFiles, overrideFiles []*File, call StaticModuleCall, sourc
|
||||
mod.ProviderRequirements.RequiredProviders[name] = rp
|
||||
}
|
||||
}
|
||||
for _, override := range file.PreconfiguredProviders {
|
||||
for name, pp := range override.PreconfiguredProviders {
|
||||
mod.PreconfiguredProviders.PreconfiguredProviders[name] = pp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range primaryFiles {
|
||||
|
||||
@@ -105,6 +105,11 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost
|
||||
diags = append(diags, reqsDiags...)
|
||||
file.RequiredProviders = append(file.RequiredProviders, reqs)
|
||||
|
||||
case "preconfigured_providers":
|
||||
reqs, reqsDiags := decodePreconfiguredProvider(innerBlock)
|
||||
diags = append(diags, reqsDiags...)
|
||||
file.PreconfiguredProviders = append(file.PreconfiguredProviders, reqs)
|
||||
|
||||
case "provider_meta":
|
||||
providerCfg, cfgDiags := decodeProviderMetaBlock(innerBlock)
|
||||
diags = append(diags, cfgDiags...)
|
||||
@@ -333,6 +338,9 @@ var terraformBlockSchema = &hcl.BodySchema{
|
||||
{
|
||||
Type: "required_providers",
|
||||
},
|
||||
{
|
||||
Type: "preconfigured_providers",
|
||||
},
|
||||
{
|
||||
Type: "provider_meta",
|
||||
LabelNames: []string{"provider"},
|
||||
|
||||
253
internal/configs/provider_preconfigured.go
Normal file
253
internal/configs/provider_preconfigured.go
Normal file
@@ -0,0 +1,253 @@
|
||||
// Copyright (c) The OpenTofu Authors
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) 2023 HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package configs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type PreconfiguredProvider struct {
|
||||
Name string
|
||||
Addr string
|
||||
ProtocolVersion int
|
||||
Type addrs.Provider
|
||||
DeclRange hcl.Range
|
||||
Aliases []addrs.LocalProviderConfig
|
||||
}
|
||||
|
||||
type PreconfiguredProviders struct {
|
||||
PreconfiguredProviders map[addrs.Provider]*PreconfiguredProvider
|
||||
DeclRange hcl.Range
|
||||
}
|
||||
|
||||
func decodePreconfiguredProvider(block *hcl.Block) (*PreconfiguredProviders, hcl.Diagnostics) {
|
||||
ret := &PreconfiguredProviders{
|
||||
PreconfiguredProviders: make(map[addrs.Provider]*PreconfiguredProvider),
|
||||
DeclRange: block.DefRange,
|
||||
}
|
||||
|
||||
attrs, diags := block.Body.JustAttributes()
|
||||
if diags.HasErrors() {
|
||||
// Returns an empty RequiredProvider to allow further validations to work properly,
|
||||
// allowing to return all the diagnostics correctly.
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
for name, attr := range attrs {
|
||||
rp := &PreconfiguredProvider{
|
||||
Name: name,
|
||||
DeclRange: attr.Expr.Range(),
|
||||
}
|
||||
provAddr, addrsDiags := addrs.ParseProviderSourceString(name)
|
||||
if addrsDiags.HasErrors() {
|
||||
for _, diagnostic := range addrsDiags.ToHCL() {
|
||||
diags = diags.Append(diagnostic)
|
||||
}
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
// Look for a single static string, in case we have the legacy version-only
|
||||
// format in the configuration.
|
||||
if expr, err := attr.Expr.Value(nil); err == nil && expr.Type().IsPrimitiveType() {
|
||||
pType, err := addrs.ParseProviderPart(rp.Name)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid provider name",
|
||||
Detail: err.Error(),
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
rp.Type = addrs.ImpliedProviderForUnqualifiedType(pType)
|
||||
ret.PreconfiguredProviders[provAddr] = rp
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// verify that the local name is already localized or produce an error.
|
||||
nameDiags := checkProviderNameNormalized(name, attr.Expr.Range())
|
||||
if nameDiags.HasErrors() {
|
||||
diags = append(diags, nameDiags...)
|
||||
continue
|
||||
}
|
||||
|
||||
kvs, mapDiags := hcl.ExprMap(attr.Expr)
|
||||
if mapDiags.HasErrors() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid required_providers object",
|
||||
Detail: "required_providers entries must be strings or objects.",
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
LOOP:
|
||||
for _, kv := range kvs {
|
||||
key, keyDiags := kv.Key.Value(nil)
|
||||
if keyDiags.HasErrors() {
|
||||
diags = append(diags, keyDiags...)
|
||||
continue
|
||||
}
|
||||
|
||||
if key.Type() != cty.String {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid Attribute",
|
||||
Detail: fmt.Sprintf("Invalid attribute value for provider requirement: %#v", key),
|
||||
Subject: kv.Key.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
switch key.AsString() {
|
||||
case "source":
|
||||
source, err := kv.Value.Value(nil)
|
||||
if err != nil || !source.Type().Equals(cty.String) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid source",
|
||||
Detail: "Source must be specified as a string.",
|
||||
Subject: kv.Value.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
fqn, sourceDiags := addrs.ParseProviderSourceString(source.AsString())
|
||||
if sourceDiags.HasErrors() {
|
||||
hclDiags := sourceDiags.ToHCL()
|
||||
// The diagnostics from ParseProviderSourceString don't contain
|
||||
// source location information because it has no context to compute
|
||||
// them from, and so we'll add those in quickly here before we
|
||||
// return.
|
||||
for _, diag := range hclDiags {
|
||||
if diag.Subject == nil {
|
||||
diag.Subject = kv.Value.Range().Ptr()
|
||||
}
|
||||
}
|
||||
diags = append(diags, hclDiags...)
|
||||
continue
|
||||
}
|
||||
|
||||
provAddr = fqn
|
||||
rp.Type = fqn
|
||||
|
||||
case "addr":
|
||||
addr, err := kv.Value.Value(nil)
|
||||
if err != nil || !addr.Type().Equals(cty.String) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid addr",
|
||||
Detail: "Addr must be specified as a string.",
|
||||
Subject: kv.Value.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
rp.Addr = addr.AsString()
|
||||
case "protocol_version":
|
||||
vers, err := kv.Value.Value(nil)
|
||||
if err != nil || !vers.Type().Equals(cty.Number) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid protocol_version",
|
||||
Detail: "protocol_version must be specified as a number.",
|
||||
Subject: kv.Value.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
versF := vers.AsBigFloat()
|
||||
if !versF.IsInt() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid protocol_version",
|
||||
Detail: "protocol_version must be specified as an integer.",
|
||||
Subject: kv.Value.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
versI, _ := versF.Int64()
|
||||
rp.ProtocolVersion = int(versI)
|
||||
|
||||
case "configuration_aliases":
|
||||
exprs, listDiags := hcl.ExprList(kv.Value)
|
||||
if listDiags.HasErrors() {
|
||||
diags = append(diags, listDiags...)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, expr := range exprs {
|
||||
traversal, travDiags := hcl.AbsTraversalForExpr(expr)
|
||||
if travDiags.HasErrors() {
|
||||
diags = append(diags, travDiags...)
|
||||
continue
|
||||
}
|
||||
|
||||
addr, cfgDiags := ParseProviderConfigCompact(traversal)
|
||||
if cfgDiags.HasErrors() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid configuration_aliases value",
|
||||
Detail: `Configuration aliases can only contain references to local provider configuration names in the format of provider.alias`,
|
||||
Subject: kv.Value.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if addr.LocalName != name {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid configuration_aliases value",
|
||||
Detail: fmt.Sprintf(`Configuration aliases must be prefixed with the provider name. Expected %q, but found %q.`, name, addr.LocalName),
|
||||
Subject: kv.Value.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
rp.Aliases = append(rp.Aliases, addr)
|
||||
}
|
||||
|
||||
default:
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid required_providers object",
|
||||
Detail: `required_providers objects can only contain "version", "source" and "configuration_aliases" attributes. To configure a provider, use a "provider" block.`,
|
||||
Subject: kv.Key.Range().Ptr(),
|
||||
})
|
||||
break LOOP
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if diags.HasErrors() {
|
||||
continue
|
||||
}
|
||||
|
||||
// We can add the required provider when there are no errors.
|
||||
// If a source was not given, create an implied type.
|
||||
if rp.Type.IsZero() {
|
||||
pType, err := addrs.ParseProviderPart(rp.Name)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid provider name",
|
||||
Detail: err.Error(),
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
} else {
|
||||
rp.Type = addrs.ImpliedProviderForUnqualifiedType(pType)
|
||||
}
|
||||
}
|
||||
|
||||
ret.PreconfiguredProviders[provAddr] = rp
|
||||
}
|
||||
|
||||
return ret, diags
|
||||
}
|
||||
@@ -77,6 +77,9 @@ type GRPCProvider struct {
|
||||
// to use as the parent context for gRPC API calls.
|
||||
ctx context.Context
|
||||
|
||||
// This is a flag to know not to call methods that are not meant for the externally managed providers
|
||||
Preconfigured bool
|
||||
|
||||
mu sync.Mutex
|
||||
// schema stores the schema for this provider. This is used to properly
|
||||
// serialize the requests for schemas.
|
||||
@@ -186,6 +189,11 @@ func (p *GRPCProvider) GetProviderSchema(ctx context.Context) (resp providers.Ge
|
||||
func (p *GRPCProvider) ValidateProviderConfig(ctx context.Context, r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
||||
logger.Trace("GRPCProvider: ValidateProviderConfig")
|
||||
|
||||
if p.Preconfigured {
|
||||
logger.Info("GRPCProvider: ValidateProviderConfig call skipped since this is a preconfigured provider")
|
||||
return resp
|
||||
}
|
||||
|
||||
schema := p.GetProviderSchema(ctx)
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
resp.Diagnostics = schema.Diagnostics
|
||||
@@ -342,6 +350,11 @@ func (p *GRPCProvider) UpgradeResourceState(ctx context.Context, r providers.Upg
|
||||
func (p *GRPCProvider) ConfigureProvider(ctx context.Context, r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
||||
logger.Trace("GRPCProvider: ConfigureProvider")
|
||||
|
||||
if p.Preconfigured {
|
||||
logger.Info("GRPCProvider.v6: ConfigureProvider call skipped since this is a preconfigured provider")
|
||||
return resp
|
||||
}
|
||||
|
||||
schema := p.GetProviderSchema(ctx)
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
resp.Diagnostics = schema.Diagnostics
|
||||
|
||||
@@ -77,6 +77,9 @@ type GRPCProvider struct {
|
||||
// to use as the parent context for gRPC API calls.
|
||||
ctx context.Context
|
||||
|
||||
// This is a flag to know not to call methods that are not meant for the externally managed providers
|
||||
Preconfigured bool
|
||||
|
||||
mu sync.Mutex
|
||||
// schema stores the schema for this provider. This is used to properly
|
||||
// serialize the requests for schemas.
|
||||
@@ -186,6 +189,11 @@ func (p *GRPCProvider) GetProviderSchema(ctx context.Context) (resp providers.Ge
|
||||
func (p *GRPCProvider) ValidateProviderConfig(ctx context.Context, r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
||||
logger.Trace("GRPCProvider.v6: ValidateProviderConfig")
|
||||
|
||||
if p.Preconfigured {
|
||||
logger.Info("GRPCProvider.v6: ValidateProviderConfig call skipped since this is a preconfigured provider")
|
||||
return resp
|
||||
}
|
||||
|
||||
schema := p.GetProviderSchema(ctx)
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
resp.Diagnostics = schema.Diagnostics
|
||||
@@ -335,6 +343,11 @@ func (p *GRPCProvider) UpgradeResourceState(ctx context.Context, r providers.Upg
|
||||
func (p *GRPCProvider) ConfigureProvider(ctx context.Context, r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
||||
logger.Trace("GRPCProvider.v6: ConfigureProvider")
|
||||
|
||||
if p.Preconfigured {
|
||||
logger.Info("GRPCProvider.v6: ConfigureProvider call skipped since this is a preconfigured provider")
|
||||
return resp
|
||||
}
|
||||
|
||||
schema := p.GetProviderSchema(ctx)
|
||||
|
||||
var mp []byte
|
||||
|
||||
Reference in New Issue
Block a user