Remove terraform.ResourceProvider, use providercache.Installer instead
Back when we first introduced provider versioning in Terraform 0.10, we did the provider version resolution in terraform.NewContext because we weren't sure yet how exactly our versioning model was going to play out (whether different versions could be selected per provider configuration, for example) and because we were building around the limitations of our existing filesystem-based plugin discovery model. However, the new installer codepath is new able to do all of the selections up front during installation, so we don't need such a heavy inversion of control abstraction to get this done: the command package can select the exact provider versions and pass their factories directly to terraform.NewContext as a simple static map. The result of this commit is that CLI commands other than "init" are now able to consume the local cache directory and selections produced by the installation process in "terraform init", passing all of the selected providers down to the terraform.NewContext function for use in implementing the main operations. This commit is just enough to get the providers passing into the terraform.Context. There's still plenty more to do here, including to repair all of the tests this change has additionally broken.
This commit is contained in:
parent
5aa2e5ec8c
commit
549aede792
|
@ -112,11 +112,9 @@ func TestLocalProvider(t *testing.T, b *Local, name string, schema *terraform.Pr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup our provider
|
// Setup our provider
|
||||||
b.ContextOpts.ProviderResolver = providers.ResolverFixed(
|
b.ContextOpts.Providers = map[addrs.Provider]providers.Factory{
|
||||||
map[addrs.Provider]providers.Factory{
|
|
||||||
addrs.NewLegacyProvider(name): providers.FactoryFixed(p),
|
addrs.NewLegacyProvider(name): providers.FactoryFixed(p),
|
||||||
},
|
}
|
||||||
)
|
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
|
@ -206,7 +206,7 @@ func TestApply_parallelism(t *testing.T) {
|
||||||
providerFactories[addrs.NewLegacyProvider(name)] = providers.FactoryFixed(provider)
|
providerFactories[addrs.NewLegacyProvider(name)] = providers.FactoryFixed(provider)
|
||||||
}
|
}
|
||||||
testingOverrides := &testingOverrides{
|
testingOverrides := &testingOverrides{
|
||||||
ProviderResolver: providers.ResolverFixed(providerFactories),
|
Providers: providerFactories,
|
||||||
}
|
}
|
||||||
|
|
||||||
ui := new(cli.MockUi)
|
ui := new(cli.MockUi)
|
||||||
|
|
|
@ -119,21 +119,17 @@ func testFixturePath(name string) string {
|
||||||
|
|
||||||
func metaOverridesForProvider(p providers.Interface) *testingOverrides {
|
func metaOverridesForProvider(p providers.Interface) *testingOverrides {
|
||||||
return &testingOverrides{
|
return &testingOverrides{
|
||||||
ProviderResolver: providers.ResolverFixed(
|
Providers: map[addrs.Provider]providers.Factory{
|
||||||
map[addrs.Provider]providers.Factory{
|
|
||||||
addrs.NewLegacyProvider("test"): providers.FactoryFixed(p),
|
addrs.NewLegacyProvider("test"): providers.FactoryFixed(p),
|
||||||
},
|
},
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func metaOverridesForProviderAndProvisioner(p providers.Interface, pr provisioners.Interface) *testingOverrides {
|
func metaOverridesForProviderAndProvisioner(p providers.Interface, pr provisioners.Interface) *testingOverrides {
|
||||||
return &testingOverrides{
|
return &testingOverrides{
|
||||||
ProviderResolver: providers.ResolverFixed(
|
Providers: map[addrs.Provider]providers.Factory{
|
||||||
map[addrs.Provider]providers.Factory{
|
|
||||||
addrs.NewLegacyProvider("test"): providers.FactoryFixed(p),
|
addrs.NewLegacyProvider("test"): providers.FactoryFixed(p),
|
||||||
},
|
},
|
||||||
),
|
|
||||||
Provisioners: map[string]provisioners.Factory{
|
Provisioners: map[string]provisioners.Factory{
|
||||||
"shell": provisioners.FactoryFixed(pr),
|
"shell": provisioners.FactoryFixed(pr),
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"github.com/hashicorp/terraform/internal/getproviders"
|
"github.com/hashicorp/terraform/internal/getproviders"
|
||||||
"github.com/hashicorp/terraform/internal/initwd"
|
"github.com/hashicorp/terraform/internal/initwd"
|
||||||
"github.com/hashicorp/terraform/internal/providercache"
|
"github.com/hashicorp/terraform/internal/providercache"
|
||||||
"github.com/hashicorp/terraform/plugin/discovery"
|
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
)
|
)
|
||||||
|
@ -32,12 +31,6 @@ type InitCommand struct {
|
||||||
|
|
||||||
// getPlugins is for the -get-plugins flag
|
// getPlugins is for the -get-plugins flag
|
||||||
getPlugins bool
|
getPlugins bool
|
||||||
|
|
||||||
// providerInstaller is used to download and install providers that
|
|
||||||
// aren't found locally. This uses a discovery.ProviderInstaller instance
|
|
||||||
// by default, but it can be overridden here as a way to mock fetching
|
|
||||||
// providers for tests.
|
|
||||||
providerInstaller discovery.Installer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitCommand) Run(args []string) int {
|
func (c *InitCommand) Run(args []string) int {
|
||||||
|
@ -73,18 +66,6 @@ func (c *InitCommand) Run(args []string) int {
|
||||||
c.getPlugins = false
|
c.getPlugins = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// set providerInstaller if we don't have a test version already
|
|
||||||
if c.providerInstaller == nil {
|
|
||||||
c.providerInstaller = &discovery.ProviderInstaller{
|
|
||||||
Dir: c.pluginDir(),
|
|
||||||
Cache: c.pluginCache(),
|
|
||||||
PluginProtocolVersion: discovery.PluginInstallProtocolVersion,
|
|
||||||
SkipVerify: !flagVerifyPlugins,
|
|
||||||
Ui: c.Ui,
|
|
||||||
Services: c.Services,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the arg count
|
// Validate the arg count
|
||||||
args = cmdFlags.Args()
|
args = cmdFlags.Args()
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
|
@ -456,15 +437,9 @@ func (c *InitCommand) getProviders(earlyConfig *earlyconfig.Config, state *state
|
||||||
|
|
||||||
// TODO: If the user gave at least one -plugin-dir option on the command
|
// TODO: If the user gave at least one -plugin-dir option on the command
|
||||||
// line, we should construct a one-off getproviders.Source that consults
|
// line, we should construct a one-off getproviders.Source that consults
|
||||||
// only those directories and use that instead of c.providerInstallSource()
|
// only those directories and pass that to c.providerInstallerCustomSource
|
||||||
// here.
|
// instead.
|
||||||
targetDir := c.providerLocalCacheDir()
|
inst := c.providerInstaller()
|
||||||
globalCacheDir := c.providerGlobalCacheDir()
|
|
||||||
source := c.providerInstallSource()
|
|
||||||
inst := providercache.NewInstaller(targetDir, source)
|
|
||||||
if globalCacheDir != nil {
|
|
||||||
inst.SetGlobalCacheDir(globalCacheDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Because we're currently just streaming a series of events sequentially
|
// Because we're currently just streaming a series of events sequentially
|
||||||
// into the terminal, we're showing only a subset of the events to keep
|
// into the terminal, we're showing only a subset of the events to keep
|
||||||
|
|
|
@ -187,7 +187,7 @@ type PluginOverrides struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type testingOverrides struct {
|
type testingOverrides struct {
|
||||||
ProviderResolver providers.Resolver
|
Providers map[addrs.Provider]providers.Factory
|
||||||
Provisioners map[string]provisioners.Factory
|
Provisioners map[string]provisioners.Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,10 +350,22 @@ func (m *Meta) contextOpts() *terraform.ContextOpts {
|
||||||
// and just work with what we've been given, thus allowing the tests
|
// and just work with what we've been given, thus allowing the tests
|
||||||
// to provide mock providers and provisioners.
|
// to provide mock providers and provisioners.
|
||||||
if m.testingOverrides != nil {
|
if m.testingOverrides != nil {
|
||||||
opts.ProviderResolver = m.testingOverrides.ProviderResolver
|
opts.Providers = m.testingOverrides.Providers
|
||||||
opts.Provisioners = m.testingOverrides.Provisioners
|
opts.Provisioners = m.testingOverrides.Provisioners
|
||||||
} else {
|
} else {
|
||||||
opts.ProviderResolver = m.providerResolver()
|
providerFactories, err := m.providerFactories()
|
||||||
|
if err != nil {
|
||||||
|
// providerFactories can fail if the plugin selections file is
|
||||||
|
// invalid in some way, but we don't have any way to report that
|
||||||
|
// from here so we'll just behave as if no providers are available
|
||||||
|
// in that case. However, we will produce a warning in case this
|
||||||
|
// shows up unexpectedly and prompts a bug report.
|
||||||
|
// This situation shouldn't arise commonly in practice because
|
||||||
|
// the selections file is generated programmatically.
|
||||||
|
log.Printf("[WARN] Failed to determine selected providers: %s", err)
|
||||||
|
providerFactories = nil
|
||||||
|
}
|
||||||
|
opts.Providers = providerFactories
|
||||||
opts.Provisioners = m.provisionerFactories()
|
opts.Provisioners = m.provisionerFactories()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,66 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
hclog "github.com/hashicorp/go-hclog"
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
terraformProvider "github.com/hashicorp/terraform/builtin/providers/terraform"
|
||||||
"github.com/hashicorp/terraform/internal/getproviders"
|
"github.com/hashicorp/terraform/internal/getproviders"
|
||||||
"github.com/hashicorp/terraform/internal/providercache"
|
"github.com/hashicorp/terraform/internal/providercache"
|
||||||
|
tfplugin "github.com/hashicorp/terraform/plugin"
|
||||||
|
"github.com/hashicorp/terraform/providers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The TF_DISABLE_PLUGIN_TLS environment variable is intended only for use by
|
||||||
|
// the plugin SDK test framework, to reduce startup overhead when rapidly
|
||||||
|
// launching and killing lots of instances of the same provider.
|
||||||
|
//
|
||||||
|
// This is not intended to be set by end-users.
|
||||||
|
var enableProviderAutoMTLS = os.Getenv("TF_DISABLE_PLUGIN_TLS") == ""
|
||||||
|
|
||||||
|
// providerInstaller returns an object that knows how to install providers and
|
||||||
|
// how to recover the selections from a prior installation process.
|
||||||
|
//
|
||||||
|
// The resulting provider installer is constructed from the results of
|
||||||
|
// the other methods providerLocalCacheDir, providerGlobalCacheDir, and
|
||||||
|
// providerInstallSource.
|
||||||
|
//
|
||||||
|
// Only one object returned from this method should be live at any time,
|
||||||
|
// because objects inside contain caches that must be maintained properly.
|
||||||
|
// Because this method wraps a result from providerLocalCacheDir, that
|
||||||
|
// limitation applies also to results from that method.
|
||||||
|
func (m *Meta) providerInstaller() *providercache.Installer {
|
||||||
|
return m.providerInstallerCustomSource(m.providerInstallSource())
|
||||||
|
}
|
||||||
|
|
||||||
|
// providerInstallerCustomSource is a variant of providerInstaller that
|
||||||
|
// allows the caller to specify a different installation source than the one
|
||||||
|
// that would naturally be selected.
|
||||||
|
//
|
||||||
|
// The result of this method has the same dependencies and constraints as
|
||||||
|
// providerInstaller.
|
||||||
|
//
|
||||||
|
// The result of providerInstallerCustomSource differs from
|
||||||
|
// providerInstaller only in how it determines package installation locations
|
||||||
|
// during EnsureProviderVersions. A caller that doesn't call
|
||||||
|
// EnsureProviderVersions (anything other than "terraform init") can safely
|
||||||
|
// just use the providerInstaller method unconditionally.
|
||||||
|
func (m *Meta) providerInstallerCustomSource(source getproviders.Source) *providercache.Installer {
|
||||||
|
targetDir := m.providerLocalCacheDir()
|
||||||
|
globalCacheDir := m.providerGlobalCacheDir()
|
||||||
|
inst := providercache.NewInstaller(targetDir, source)
|
||||||
|
if globalCacheDir != nil {
|
||||||
|
inst.SetGlobalCacheDir(globalCacheDir)
|
||||||
|
}
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
// providerLocalCacheDir returns an object representing the
|
// providerLocalCacheDir returns an object representing the
|
||||||
// configuration-specific local cache directory. This is the
|
// configuration-specific local cache directory. This is the
|
||||||
// only location consulted for provider plugin packages for Terraform
|
// only location consulted for provider plugin packages for Terraform
|
||||||
|
@ -15,6 +69,9 @@ import (
|
||||||
// Only the provider installer (in "terraform init") is permitted to make
|
// Only the provider installer (in "terraform init") is permitted to make
|
||||||
// modifications to this cache directory. All other commands must treat it
|
// modifications to this cache directory. All other commands must treat it
|
||||||
// as read-only.
|
// as read-only.
|
||||||
|
//
|
||||||
|
// Only one object returned from this method should be live at any time,
|
||||||
|
// because objects inside contain caches that must be maintained properly.
|
||||||
func (m *Meta) providerLocalCacheDir() *providercache.Dir {
|
func (m *Meta) providerLocalCacheDir() *providercache.Dir {
|
||||||
dir := filepath.Join(m.DataDir(), "plugins")
|
dir := filepath.Join(m.DataDir(), "plugins")
|
||||||
if dir == "" {
|
if dir == "" {
|
||||||
|
@ -30,6 +87,9 @@ func (m *Meta) providerLocalCacheDir() *providercache.Dir {
|
||||||
// This function may return nil, in which case there is no global cache
|
// This function may return nil, in which case there is no global cache
|
||||||
// configured and new packages should be downloaded directly into individual
|
// configured and new packages should be downloaded directly into individual
|
||||||
// configuration-specific cache directories.
|
// configuration-specific cache directories.
|
||||||
|
//
|
||||||
|
// Only one object returned from this method should be live at any time,
|
||||||
|
// because objects inside contain caches that must be maintained properly.
|
||||||
func (m *Meta) providerGlobalCacheDir() *providercache.Dir {
|
func (m *Meta) providerGlobalCacheDir() *providercache.Dir {
|
||||||
dir := m.PluginCacheDir
|
dir := m.PluginCacheDir
|
||||||
if dir == "" {
|
if dir == "" {
|
||||||
|
@ -61,3 +121,85 @@ func (m *Meta) providerInstallSource() getproviders.Source {
|
||||||
}
|
}
|
||||||
return m.ProviderSource
|
return m.ProviderSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// providerFactories uses the selections made previously by an installer in
|
||||||
|
// the local cache directory (m.providerLocalCacheDir) to produce a map
|
||||||
|
// from provider addresses to factory functions to create instances of
|
||||||
|
// those providers.
|
||||||
|
//
|
||||||
|
// providerFactories will return an error if the installer's selections cannot
|
||||||
|
// be honored with what is currently in the cache, such as if a selected
|
||||||
|
// package has been removed from the cache or if the contents of a selected
|
||||||
|
// package have been modified outside of the installer. If it returns an error,
|
||||||
|
// the returned map may be incomplete or invalid.
|
||||||
|
func (m *Meta) providerFactories() (map[addrs.Provider]providers.Factory, error) {
|
||||||
|
// We don't have to worry about potentially calling
|
||||||
|
// providerInstallerCustomSource here because we're only using this
|
||||||
|
// installer for its SelectedPackages method, which does not consult
|
||||||
|
// any provider sources.
|
||||||
|
inst := m.providerInstaller()
|
||||||
|
selected, err := inst.SelectedPackages()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to recall provider packages selected by earlier 'terraform init': %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The internal providers are _always_ available, even if the configuration
|
||||||
|
// doesn't request them, because they don't need any special installation
|
||||||
|
// and they'll just be ignored if not used.
|
||||||
|
internalFactories := m.internalProviders()
|
||||||
|
|
||||||
|
factories := make(map[addrs.Provider]providers.Factory, len(selected)+len(internalFactories))
|
||||||
|
for name, factory := range internalFactories {
|
||||||
|
factories[addrs.NewBuiltInProvider(name)] = factory
|
||||||
|
}
|
||||||
|
for provider, cached := range selected {
|
||||||
|
factories[provider] = providerFactory(cached)
|
||||||
|
}
|
||||||
|
return factories, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Meta) internalProviders() map[string]providers.Factory {
|
||||||
|
return map[string]providers.Factory{
|
||||||
|
"terraform": func() (providers.Interface, error) {
|
||||||
|
return terraformProvider.NewProvider(), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// providerFactory produces a provider factory that runs up the executable
|
||||||
|
// file in the given cache package and uses go-plugin to implement
|
||||||
|
// providers.Interface against it.
|
||||||
|
func providerFactory(meta *providercache.CachedProvider) providers.Factory {
|
||||||
|
return func() (providers.Interface, error) {
|
||||||
|
logger := hclog.New(&hclog.LoggerOptions{
|
||||||
|
Name: "plugin",
|
||||||
|
Level: hclog.Trace,
|
||||||
|
Output: os.Stderr,
|
||||||
|
})
|
||||||
|
|
||||||
|
config := &plugin.ClientConfig{
|
||||||
|
Cmd: exec.Command(meta.ExecutableFile),
|
||||||
|
HandshakeConfig: tfplugin.Handshake,
|
||||||
|
VersionedPlugins: tfplugin.VersionedPlugins,
|
||||||
|
Managed: true,
|
||||||
|
Logger: logger,
|
||||||
|
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
|
||||||
|
AutoMTLS: enableProviderAutoMTLS,
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the client so that the plugin can kill the child process
|
||||||
|
p := raw.(*tfplugin.GRPCProvider)
|
||||||
|
p.PluginClient = client
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
@ -15,104 +14,19 @@ import (
|
||||||
plugin "github.com/hashicorp/go-plugin"
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
"github.com/kardianos/osext"
|
"github.com/kardianos/osext"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
|
||||||
terraformProvider "github.com/hashicorp/terraform/builtin/providers/terraform"
|
|
||||||
tfplugin "github.com/hashicorp/terraform/plugin"
|
tfplugin "github.com/hashicorp/terraform/plugin"
|
||||||
"github.com/hashicorp/terraform/plugin/discovery"
|
"github.com/hashicorp/terraform/plugin/discovery"
|
||||||
"github.com/hashicorp/terraform/providers"
|
|
||||||
"github.com/hashicorp/terraform/provisioners"
|
"github.com/hashicorp/terraform/provisioners"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
// multiVersionProviderResolver is an implementation of
|
// NOTE WELL: The logic in this file is primarily about plugin types OTHER THAN
|
||||||
// terraform.ResourceProviderResolver that matches the given version constraints
|
// providers, which use an older set of approaches implemented here.
|
||||||
// against a set of versioned provider plugins to find the newest version of
|
//
|
||||||
// each that satisfies the given constraints.
|
// The provider-related functions live primarily in meta_providers.go, and
|
||||||
type multiVersionProviderResolver struct {
|
// lean on some different underlying mechanisms in order to support automatic
|
||||||
Available discovery.PluginMetaSet
|
// installation and a heirarchical addressing namespace, neither of which
|
||||||
|
// are supported for other plugin types.
|
||||||
// Internal is a map that overrides the usual plugin selection process
|
|
||||||
// for internal plugins. These plugins do not support version constraints
|
|
||||||
// (will produce an error if one is set). This should be used only in
|
|
||||||
// exceptional circumstances since it forces the provider's release
|
|
||||||
// schedule to be tied to that of Terraform Core.
|
|
||||||
Internal map[addrs.Provider]providers.Factory
|
|
||||||
}
|
|
||||||
|
|
||||||
func chooseProviders(avail discovery.PluginMetaSet, internal map[addrs.Provider]providers.Factory, reqd discovery.PluginRequirements) map[string]discovery.PluginMeta {
|
|
||||||
candidates := avail.ConstrainVersions(reqd)
|
|
||||||
ret := map[string]discovery.PluginMeta{}
|
|
||||||
for name, metas := range candidates {
|
|
||||||
// If the provider is in our internal map then we ignore any
|
|
||||||
// discovered plugins for it since these are dealt with separately.
|
|
||||||
if _, isInternal := internal[addrs.NewLegacyProvider(name)]; isInternal {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(metas) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ret[name] = metas.Newest()
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *multiVersionProviderResolver) ResolveProviders(
|
|
||||||
reqd discovery.PluginRequirements,
|
|
||||||
) (map[addrs.Provider]providers.Factory, []error) {
|
|
||||||
factories := make(map[addrs.Provider]providers.Factory, len(reqd))
|
|
||||||
var errs []error
|
|
||||||
|
|
||||||
chosen := chooseProviders(r.Available, r.Internal, reqd)
|
|
||||||
for name, req := range reqd {
|
|
||||||
if factory, isInternal := r.Internal[addrs.NewLegacyProvider(name)]; isInternal {
|
|
||||||
if !req.Versions.Unconstrained() {
|
|
||||||
errs = append(errs, fmt.Errorf("provider.%s: this provider is built in to Terraform and so it does not support version constraints", name))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
factories[addrs.NewLegacyProvider(name)] = factory
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if newest, available := chosen[name]; available {
|
|
||||||
digest, err := newest.SHA256()
|
|
||||||
if err != nil {
|
|
||||||
errs = append(errs, fmt.Errorf("provider.%s: failed to load plugin to verify its signature: %s", name, err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !reqd[name].AcceptsSHA256(digest) {
|
|
||||||
errs = append(errs, fmt.Errorf("provider.%s: new or changed plugin executable", name))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
factories[addrs.NewLegacyProvider(name)] = providerFactory(newest)
|
|
||||||
} else {
|
|
||||||
msg := fmt.Sprintf("provider.%s: no suitable version installed", name)
|
|
||||||
|
|
||||||
required := req.Versions.String()
|
|
||||||
// no version is unconstrained
|
|
||||||
if required == "" {
|
|
||||||
required = "(any version)"
|
|
||||||
}
|
|
||||||
|
|
||||||
foundVersions := []string{}
|
|
||||||
for meta := range r.Available.WithName(name) {
|
|
||||||
foundVersions = append(foundVersions, fmt.Sprintf("%q", meta.Version))
|
|
||||||
}
|
|
||||||
|
|
||||||
found := "none"
|
|
||||||
if len(foundVersions) > 0 {
|
|
||||||
found = strings.Join(foundVersions, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
msg += fmt.Sprintf("\n version requirements: %q\n versions installed: %s", required, found)
|
|
||||||
|
|
||||||
errs = append(errs, errors.New(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return factories, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
// store the user-supplied path for plugin discovery
|
// store the user-supplied path for plugin discovery
|
||||||
func (m *Meta) storePluginPath(pluginPath []string) error {
|
func (m *Meta) storePluginPath(pluginPath []string) error {
|
||||||
|
@ -216,101 +130,6 @@ func (m *Meta) pluginCache() discovery.PluginCache {
|
||||||
return discovery.NewLocalPluginCache(dir)
|
return discovery.NewLocalPluginCache(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// providerPluginSet returns the set of valid providers that were discovered in
|
|
||||||
// the defined search paths.
|
|
||||||
func (m *Meta) providerPluginSet() discovery.PluginMetaSet {
|
|
||||||
plugins := discovery.FindPlugins("provider", m.pluginDirs(true))
|
|
||||||
|
|
||||||
// Add providers defined in the legacy .terraformrc,
|
|
||||||
if m.PluginOverrides != nil {
|
|
||||||
for k, v := range m.PluginOverrides.Providers {
|
|
||||||
log.Printf("[DEBUG] found plugin override in .terraformrc: %q, %q", k, v)
|
|
||||||
}
|
|
||||||
plugins = plugins.OverridePaths(m.PluginOverrides.Providers)
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins, _ = plugins.ValidateVersions()
|
|
||||||
|
|
||||||
for p := range plugins {
|
|
||||||
log.Printf("[DEBUG] found valid plugin: %q, %q, %q", p.Name, p.Version, p.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return plugins
|
|
||||||
}
|
|
||||||
|
|
||||||
// providerPluginAutoInstalledSet returns the set of providers that exist
|
|
||||||
// within the auto-install directory.
|
|
||||||
func (m *Meta) providerPluginAutoInstalledSet() discovery.PluginMetaSet {
|
|
||||||
plugins := discovery.FindPlugins("provider", []string{m.pluginDir()})
|
|
||||||
plugins, _ = plugins.ValidateVersions()
|
|
||||||
|
|
||||||
for p := range plugins {
|
|
||||||
log.Printf("[DEBUG] found valid plugin: %q", p.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return plugins
|
|
||||||
}
|
|
||||||
|
|
||||||
// providerPluginManuallyInstalledSet returns the set of providers that exist
|
|
||||||
// in all locations *except* the auto-install directory.
|
|
||||||
func (m *Meta) providerPluginManuallyInstalledSet() discovery.PluginMetaSet {
|
|
||||||
plugins := discovery.FindPlugins("provider", m.pluginDirs(false))
|
|
||||||
|
|
||||||
// Add providers defined in the legacy .terraformrc,
|
|
||||||
if m.PluginOverrides != nil {
|
|
||||||
for k, v := range m.PluginOverrides.Providers {
|
|
||||||
log.Printf("[DEBUG] found plugin override in .terraformrc: %q, %q", k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins = plugins.OverridePaths(m.PluginOverrides.Providers)
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins, _ = plugins.ValidateVersions()
|
|
||||||
|
|
||||||
for p := range plugins {
|
|
||||||
log.Printf("[DEBUG] found valid plugin: %q, %q, %q", p.Name, p.Version, p.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return plugins
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Meta) providerResolver() providers.Resolver {
|
|
||||||
return &multiVersionProviderResolver{
|
|
||||||
Available: m.providerPluginSet(),
|
|
||||||
Internal: m.internalProviders(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Meta) internalProviders() map[addrs.Provider]providers.Factory {
|
|
||||||
return map[addrs.Provider]providers.Factory{
|
|
||||||
addrs.NewLegacyProvider("terraform"): func() (providers.Interface, error) {
|
|
||||||
return terraformProvider.NewProvider(), nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter the requirements returning only the providers that we can't resolve
|
|
||||||
func (m *Meta) missingProviders(avail discovery.PluginMetaSet, reqd discovery.PluginRequirements) discovery.PluginRequirements {
|
|
||||||
missing := make(discovery.PluginRequirements)
|
|
||||||
|
|
||||||
candidates := avail.ConstrainVersions(reqd)
|
|
||||||
internal := m.internalProviders()
|
|
||||||
|
|
||||||
for name, versionSet := range reqd {
|
|
||||||
// internal providers can't be missing
|
|
||||||
if _, ok := internal[addrs.NewLegacyProvider(name)]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("[DEBUG] plugin requirements: %q=%q", name, versionSet.Versions)
|
|
||||||
if metas := candidates[name]; metas.Count() == 0 {
|
|
||||||
missing[name] = versionSet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return missing
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Meta) provisionerFactories() map[string]terraform.ProvisionerFactory {
|
func (m *Meta) provisionerFactories() map[string]terraform.ProvisionerFactory {
|
||||||
dirs := m.pluginDirs(true)
|
dirs := m.pluginDirs(true)
|
||||||
plugins := discovery.FindPlugins("provisioner", dirs)
|
plugins := discovery.FindPlugins("provisioner", dirs)
|
||||||
|
@ -364,28 +183,6 @@ func internalPluginClient(kind, name string) (*plugin.Client, error) {
|
||||||
return plugin.NewClient(cfg), nil
|
return plugin.NewClient(cfg), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func providerFactory(meta discovery.PluginMeta) providers.Factory {
|
|
||||||
return func() (providers.Interface, error) {
|
|
||||||
client := tfplugin.Client(meta)
|
|
||||||
// Request the RPC client so we can get the provider
|
|
||||||
// so we can build the actual RPC-implemented provider.
|
|
||||||
rpcClient, err := client.Client()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rpcClient.Dispense(tfplugin.ProviderPluginName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// store the client so that the plugin can kill the child process
|
|
||||||
p := raw.(*tfplugin.GRPCProvider)
|
|
||||||
p.PluginClient = client
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func provisionerFactory(meta discovery.PluginMeta) terraform.ProvisionerFactory {
|
func provisionerFactory(meta discovery.PluginMeta) terraform.ProvisionerFactory {
|
||||||
return func() (provisioners.Interface, error) {
|
return func() (provisioners.Interface, error) {
|
||||||
client := tfplugin.Client(meta)
|
client := tfplugin.Client(meta)
|
||||||
|
|
|
@ -229,11 +229,9 @@ func TestStateShow_configured_provider(t *testing.T) {
|
||||||
c := &StateShowCommand{
|
c := &StateShowCommand{
|
||||||
Meta: Meta{
|
Meta: Meta{
|
||||||
testingOverrides: &testingOverrides{
|
testingOverrides: &testingOverrides{
|
||||||
ProviderResolver: providers.ResolverFixed(
|
Providers: map[addrs.Provider]providers.Factory{
|
||||||
map[addrs.Provider]providers.Factory{
|
|
||||||
addrs.NewLegacyProvider("test-beta"): providers.FactoryFixed(p),
|
addrs.NewLegacyProvider("test-beta"): providers.FactoryFixed(p),
|
||||||
},
|
},
|
||||||
),
|
|
||||||
},
|
},
|
||||||
Ui: ui,
|
Ui: ui,
|
||||||
},
|
},
|
||||||
|
|
|
@ -55,33 +55,19 @@ func (c *VersionCommand) Run(args []string) int {
|
||||||
// Generally-speaking this is a best-effort thing that will give us a good
|
// Generally-speaking this is a best-effort thing that will give us a good
|
||||||
// result in the usual case where the user successfully ran "terraform init"
|
// result in the usual case where the user successfully ran "terraform init"
|
||||||
// and then hit a problem running _another_ command.
|
// and then hit a problem running _another_ command.
|
||||||
providerPlugins := c.providerPluginSet()
|
providerInstaller := c.providerInstaller()
|
||||||
pluginsLockFile := c.providerPluginsLock()
|
providerSelections, err := providerInstaller.SelectedPackages()
|
||||||
pluginsLock := pluginsLockFile.Read()
|
|
||||||
var pluginVersions []string
|
var pluginVersions []string
|
||||||
for meta := range providerPlugins {
|
|
||||||
name := meta.Name
|
|
||||||
wantHash, wanted := pluginsLock[name]
|
|
||||||
if !wanted {
|
|
||||||
// Ignore providers that aren't used by the current config at all
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
gotHash, err := meta.SHA256()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if we can't read the file to hash it, ignore it.
|
// we'll just ignore it and show no plugins at all, then.
|
||||||
continue
|
providerSelections = nil
|
||||||
}
|
}
|
||||||
if !bytes.Equal(gotHash, wantHash) {
|
for providerAddr, cached := range providerSelections {
|
||||||
// Not the plugin we've locked, so ignore it.
|
version := cached.Version.String()
|
||||||
continue
|
if version == "0.0.0" {
|
||||||
}
|
pluginVersions = append(pluginVersions, fmt.Sprintf("+ provider %s (unversioned)", providerAddr))
|
||||||
|
|
||||||
// If we get here then we've found a selected plugin, so we'll print
|
|
||||||
// out its details.
|
|
||||||
if meta.Version == "0.0.0" {
|
|
||||||
pluginVersions = append(pluginVersions, fmt.Sprintf("+ provider.%s (unversioned)", name))
|
|
||||||
} else {
|
} else {
|
||||||
pluginVersions = append(pluginVersions, fmt.Sprintf("+ provider.%s v%s", name, meta.Version))
|
pluginVersions = append(pluginVersions, fmt.Sprintf("+ provider %s v%s", providerAddr, version))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(pluginVersions) != 0 {
|
if len(pluginVersions) != 0 {
|
||||||
|
|
|
@ -481,10 +481,19 @@ func Test(t TestT, c TestCase) {
|
||||||
c.PreCheck()
|
c.PreCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
providerFactories, err := testProviderFactories(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// get instances of all providers, so we can use the individual
|
// get instances of all providers, so we can use the individual
|
||||||
// resources to shim the state during the tests.
|
// resources to shim the state during the tests.
|
||||||
providers := make(map[string]terraform.ResourceProvider)
|
providers := make(map[string]terraform.ResourceProvider)
|
||||||
for name, pf := range testProviderFactories(c) {
|
legacyProviderFactories, err := testProviderFactoriesLegacy(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for name, pf := range legacyProviderFactories {
|
||||||
p, err := pf()
|
p, err := pf()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -492,12 +501,7 @@ func Test(t TestT, c TestCase) {
|
||||||
providers[name] = p
|
providers[name] = p
|
||||||
}
|
}
|
||||||
|
|
||||||
providerResolver, err := testProviderResolver(c)
|
opts := terraform.ContextOpts{Providers: providerFactories}
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := terraform.ContextOpts{ProviderResolver: providerResolver}
|
|
||||||
|
|
||||||
// A single state variable to track the lifecycle, starting with no state
|
// A single state variable to track the lifecycle, starting with no state
|
||||||
var state *terraform.State
|
var state *terraform.State
|
||||||
|
@ -650,10 +654,14 @@ func testProviderConfig(c TestCase) string {
|
||||||
return strings.Join(lines, "")
|
return strings.Join(lines, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// testProviderFactories combines the fixed Providers and
|
// testProviderFactoriesLegacy is like testProviderFactories but it returns
|
||||||
// ResourceProviderFactory functions into a single map of
|
// providers implementing the legacy interface terraform.ResourceProvider,
|
||||||
// ResourceProviderFactory functions.
|
// rather than the current providers.Interface.
|
||||||
func testProviderFactories(c TestCase) map[string]terraform.ResourceProviderFactory {
|
//
|
||||||
|
// It also identifies all providers as legacy-style single names rather than
|
||||||
|
// full addresses, for compatibility with legacy code that doesn't understand
|
||||||
|
// FQNs.
|
||||||
|
func testProviderFactoriesLegacy(c TestCase) (map[string]terraform.ResourceProviderFactory, error) {
|
||||||
ctxProviders := make(map[string]terraform.ResourceProviderFactory)
|
ctxProviders := make(map[string]terraform.ResourceProviderFactory)
|
||||||
for k, pf := range c.ProviderFactories {
|
for k, pf := range c.ProviderFactories {
|
||||||
ctxProviders[k] = pf
|
ctxProviders[k] = pf
|
||||||
|
@ -663,24 +671,25 @@ func testProviderFactories(c TestCase) map[string]terraform.ResourceProviderFact
|
||||||
for k, p := range c.Providers {
|
for k, p := range c.Providers {
|
||||||
ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p)
|
ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p)
|
||||||
}
|
}
|
||||||
return ctxProviders
|
return ctxProviders, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// testProviderResolver is a helper to build a ResourceProviderResolver
|
// testProviderFactories combines the fixed Providers and
|
||||||
// with pre instantiated ResourceProviders, so that we can reset them for the
|
// ResourceProviderFactory functions into a single map of
|
||||||
// test, while only calling the factory function once.
|
// ResourceProviderFactory functions.
|
||||||
// Any errors are stored so that they can be returned by the factory in
|
func testProviderFactories(c TestCase) (map[addrs.Provider]providers.Factory, error) {
|
||||||
// terraform to match non-test behavior.
|
ctxProviders, err := testProviderFactoriesLegacy(c)
|
||||||
func testProviderResolver(c TestCase) (providers.Resolver, error) {
|
if err != nil {
|
||||||
ctxProviders := testProviderFactories(c)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// wrap the old provider factories in the test grpc server so they can be
|
// We additionally wrap all of the factories as a GRPCTestProvider, which
|
||||||
// called from terraform.
|
// allows them to appear as a new-style providers.Interface, rather than
|
||||||
|
// the legacy terraform.ResourceProvider.
|
||||||
newProviders := make(map[addrs.Provider]providers.Factory)
|
newProviders := make(map[addrs.Provider]providers.Factory)
|
||||||
|
for legacyName, pf := range ctxProviders {
|
||||||
for k, pf := range ctxProviders {
|
|
||||||
factory := pf // must copy to ensure each closure sees its own value
|
factory := pf // must copy to ensure each closure sees its own value
|
||||||
newProviders[addrs.NewLegacyProvider(k)] = func() (providers.Interface, error) {
|
newProviders[addrs.NewLegacyProvider(legacyName)] = func() (providers.Interface, error) {
|
||||||
p, err := factory()
|
p, err := factory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -693,7 +702,7 @@ func testProviderResolver(c TestCase) (providers.Resolver, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return providers.ResolverFixed(newProviders), nil
|
return newProviders, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnitTest is a helper to force the acceptance testing harness to run in the
|
// UnitTest is a helper to force the acceptance testing harness to run in the
|
||||||
|
|
|
@ -1,56 +1,5 @@
|
||||||
package providers
|
package providers
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
|
||||||
"github.com/hashicorp/terraform/plugin/discovery"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Resolver is an interface implemented by objects that are able to resolve
|
|
||||||
// a given set of resource provider version constraints into Factory
|
|
||||||
// callbacks.
|
|
||||||
type Resolver interface {
|
|
||||||
// Given a constraint map, return a Factory for each requested provider.
|
|
||||||
// If some or all of the constraints cannot be satisfied, return a non-nil
|
|
||||||
// slice of errors describing the problems.
|
|
||||||
ResolveProviders(reqd discovery.PluginRequirements) (map[addrs.Provider]Factory, []error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolverFunc wraps a callback function and turns it into a Resolver
|
|
||||||
// implementation, for convenience in situations where a function and its
|
|
||||||
// associated closure are sufficient as a resolver implementation.
|
|
||||||
type ResolverFunc func(reqd discovery.PluginRequirements) (map[addrs.Provider]Factory, []error)
|
|
||||||
|
|
||||||
// ResolveProviders implements Resolver by calling the
|
|
||||||
// wrapped function.
|
|
||||||
func (f ResolverFunc) ResolveProviders(reqd discovery.PluginRequirements) (map[addrs.Provider]Factory, []error) {
|
|
||||||
return f(reqd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolverFixed returns a Resolver that has a fixed set of provider factories
|
|
||||||
// provided by the caller. The returned resolver ignores version constraints
|
|
||||||
// entirely and just returns the given factory for each requested provider
|
|
||||||
// name.
|
|
||||||
//
|
|
||||||
// This function is primarily used in tests, to provide mock providers or
|
|
||||||
// in-process providers under test.
|
|
||||||
func ResolverFixed(factories map[addrs.Provider]Factory) Resolver {
|
|
||||||
return ResolverFunc(func(reqd discovery.PluginRequirements) (map[addrs.Provider]Factory, []error) {
|
|
||||||
ret := make(map[addrs.Provider]Factory, len(reqd))
|
|
||||||
var errs []error
|
|
||||||
for name := range reqd {
|
|
||||||
fqn := addrs.NewLegacyProvider(name)
|
|
||||||
if factory, exists := factories[fqn]; exists {
|
|
||||||
ret[fqn] = factory
|
|
||||||
} else {
|
|
||||||
errs = append(errs, fmt.Errorf("provider %q is not available", name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret, errs
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Factory is a function type that creates a new instance of a resource
|
// Factory is a function type that creates a new instance of a resource
|
||||||
// provider, or returns an error if that is impossible.
|
// provider, or returns an error if that is impossible.
|
||||||
type Factory func() (Interface, error)
|
type Factory func() (Interface, error)
|
|
@ -57,8 +57,8 @@ type ContextOpts struct {
|
||||||
|
|
||||||
Hooks []Hook
|
Hooks []Hook
|
||||||
Parallelism int
|
Parallelism int
|
||||||
ProviderResolver providers.Resolver
|
Providers map[addrs.Provider]providers.Factory
|
||||||
Provisioners map[string]ProvisionerFactory
|
Provisioners map[string]provisioners.Factory
|
||||||
|
|
||||||
// If non-nil, will apply as additional constraints on the provider
|
// If non-nil, will apply as additional constraints on the provider
|
||||||
// plugins that will be requested from the provider resolver.
|
// plugins that will be requested from the provider resolver.
|
||||||
|
@ -169,28 +169,8 @@ func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) {
|
||||||
// override the defaults.
|
// override the defaults.
|
||||||
variables = variables.Override(opts.Variables)
|
variables = variables.Override(opts.Variables)
|
||||||
|
|
||||||
// Bind available provider plugins to the constraints in config
|
|
||||||
var providerFactories map[addrs.Provider]providers.Factory
|
|
||||||
if opts.ProviderResolver != nil {
|
|
||||||
deps := ConfigTreeDependencies(opts.Config, state)
|
|
||||||
reqd := deps.AllProviderRequirements()
|
|
||||||
if opts.ProviderSHA256s != nil && !opts.SkipProviderVerify {
|
|
||||||
reqd.LockExecutables(opts.ProviderSHA256s)
|
|
||||||
}
|
|
||||||
log.Printf("[TRACE] terraform.NewContext: resolving provider version selections")
|
|
||||||
var providerDiags tfdiags.Diagnostics
|
|
||||||
providerFactories, providerDiags = resourceProviderFactories(opts.ProviderResolver, reqd)
|
|
||||||
diags = diags.Append(providerDiags)
|
|
||||||
|
|
||||||
if diags.HasErrors() {
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
providerFactories = make(map[addrs.Provider]providers.Factory)
|
|
||||||
}
|
|
||||||
|
|
||||||
components := &basicComponentFactory{
|
components := &basicComponentFactory{
|
||||||
providers: providerFactories,
|
providers: opts.Providers,
|
||||||
provisioners: opts.Provisioners,
|
provisioners: opts.Provisioners,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
// ResourceProvider is a legacy interface for providers.
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/plugin/discovery"
|
|
||||||
"github.com/hashicorp/terraform/providers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ResourceProvider is an interface that must be implemented by any
|
|
||||||
// resource provider: the thing that creates and manages the resources in
|
|
||||||
// a Terraform configuration.
|
|
||||||
//
|
//
|
||||||
// Important implementation note: All returned pointers, such as
|
// This is retained only for compatibility with legacy code. The current
|
||||||
// *ResourceConfig, *InstanceState, *InstanceDiff, etc. must not point to
|
// interface for providers is providers.Interface, in the sibling directory
|
||||||
// shared data. Terraform is highly parallel and assumes that this data is safe
|
// named "providers".
|
||||||
// to read/write in parallel so it must be unique references. Note that it is
|
|
||||||
// safe to return arguments as results, however.
|
|
||||||
type ResourceProvider interface {
|
type ResourceProvider interface {
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
* Functions related to the provider
|
* Functions related to the provider
|
||||||
|
@ -203,53 +189,6 @@ type DataSource struct {
|
||||||
SchemaAvailable bool
|
SchemaAvailable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceProviderResolver is an interface implemented by objects that are
|
|
||||||
// able to resolve a given set of resource provider version constraints
|
|
||||||
// into ResourceProviderFactory callbacks.
|
|
||||||
type ResourceProviderResolver interface {
|
|
||||||
// Given a constraint map, return a ResourceProviderFactory for each
|
|
||||||
// requested provider. If some or all of the constraints cannot be
|
|
||||||
// satisfied, return a non-nil slice of errors describing the problems.
|
|
||||||
ResolveProviders(reqd discovery.PluginRequirements) (map[addrs.Provider]ResourceProviderFactory, []error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResourceProviderResolverFunc wraps a callback function and turns it into
|
|
||||||
// a ResourceProviderResolver implementation, for convenience in situations
|
|
||||||
// where a function and its associated closure are sufficient as a resolver
|
|
||||||
// implementation.
|
|
||||||
type ResourceProviderResolverFunc func(reqd discovery.PluginRequirements) (map[addrs.Provider]ResourceProviderFactory, []error)
|
|
||||||
|
|
||||||
// ResolveProviders implements ResourceProviderResolver by calling the
|
|
||||||
// wrapped function.
|
|
||||||
func (f ResourceProviderResolverFunc) ResolveProviders(reqd discovery.PluginRequirements) (map[addrs.Provider]ResourceProviderFactory, []error) {
|
|
||||||
return f(reqd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResourceProviderResolverFixed returns a ResourceProviderResolver that
|
|
||||||
// has a fixed set of provider factories provided by the caller. The returned
|
|
||||||
// resolver ignores version constraints entirely and just returns the given
|
|
||||||
// factory for each requested provider name.
|
|
||||||
//
|
|
||||||
// This function is primarily used in tests, to provide mock providers or
|
|
||||||
// in-process providers under test.
|
|
||||||
func ResourceProviderResolverFixed(factories map[addrs.Provider]ResourceProviderFactory) ResourceProviderResolver {
|
|
||||||
return ResourceProviderResolverFunc(func(reqd discovery.PluginRequirements) (map[addrs.Provider]ResourceProviderFactory, []error) {
|
|
||||||
ret := make(map[addrs.Provider]ResourceProviderFactory, len(reqd))
|
|
||||||
var errs []error
|
|
||||||
for name := range reqd {
|
|
||||||
// FIXME: discovery.PluginRequirements should use addrs.Provider as
|
|
||||||
// the map keys instead of a string
|
|
||||||
fqn := addrs.NewLegacyProvider(name)
|
|
||||||
if factory, exists := factories[fqn]; exists {
|
|
||||||
ret[fqn] = factory
|
|
||||||
} else {
|
|
||||||
errs = append(errs, fmt.Errorf("provider %q is not available", name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret, errs
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResourceProviderFactory is a function type that creates a new instance
|
// ResourceProviderFactory is a function type that creates a new instance
|
||||||
// of a resource provider.
|
// of a resource provider.
|
||||||
type ResourceProviderFactory func() (ResourceProvider, error)
|
type ResourceProviderFactory func() (ResourceProvider, error)
|
||||||
|
@ -282,34 +221,6 @@ func ProviderHasDataSource(p ResourceProvider, n string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceProviderFactories matches available plugins to the given version
|
|
||||||
// requirements to produce a map of compatible provider plugins if possible,
|
|
||||||
// or an error if the currently-available plugins are insufficient.
|
|
||||||
//
|
|
||||||
// This should be called only with configurations that have passed calls
|
|
||||||
// to config.Validate(), which ensures that all of the given version
|
|
||||||
// constraints are valid. It will panic if any invalid constraints are present.
|
|
||||||
func resourceProviderFactories(resolver providers.Resolver, reqd discovery.PluginRequirements) (map[addrs.Provider]providers.Factory, tfdiags.Diagnostics) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
ret, errs := resolver.ResolveProviders(reqd)
|
|
||||||
if errs != nil {
|
|
||||||
diags = diags.Append(
|
|
||||||
tfdiags.Sourceless(tfdiags.Error,
|
|
||||||
"Could not satisfy plugin requirements",
|
|
||||||
errPluginInit,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, err := range errs {
|
|
||||||
diags = diags.Append(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const errPluginInit = `
|
const errPluginInit = `
|
||||||
Plugin reinitialization required. Please run "terraform init".
|
Plugin reinitialization required. Please run "terraform init".
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue