Merge of cross-cutting work for new provider naming
This merge introduces various work across the whole codebase to prepare codepaths to deal with the new decentralized provider naming scheme and to use the new provider installation codepaths that are aware of the new scheme. The incoming branch of commits here (the second commit in the merge) contains a period where the tests were not passing as a tradeoff to keep the individual changes separated while accepting that that makes that part of the history unsuitable for "git bisect" usage. If you are using git bisect on this portion of the history, exclude the commits from the incoming branch.
This commit is contained in:
commit
bc3de6e2d6
|
@ -23,6 +23,20 @@ type Provider struct {
|
|||
// not have an explicit hostname.
|
||||
const DefaultRegistryHost = svchost.Hostname("registry.terraform.io")
|
||||
|
||||
// BuiltInProviderHost is the pseudo-hostname used for the "built-in" provider
|
||||
// namespace. Built-in provider addresses must also have their namespace set
|
||||
// to BuiltInProviderNamespace in order to be considered as built-in.
|
||||
const BuiltInProviderHost = svchost.Hostname("terraform.io")
|
||||
|
||||
// BuiltInProviderNamespace is the provider namespace used for "built-in"
|
||||
// providers. Built-in provider addresses must also have their hostname
|
||||
// set to BuiltInProviderHost in order to be considered as built-in.
|
||||
//
|
||||
// The this namespace is literally named "builtin", in the hope that users
|
||||
// who see FQNs containing this will be able to infer the way in which they are
|
||||
// special, even if they haven't encountered the concept formally yet.
|
||||
const BuiltInProviderNamespace = "builtin"
|
||||
|
||||
// LegacyProviderNamespace is the special string used in the Namespace field
|
||||
// of type Provider to mark a legacy provider address. This special namespace
|
||||
// value would normally be invalid, and can be used only when the hostname is
|
||||
|
@ -30,7 +44,7 @@ const DefaultRegistryHost = svchost.Hostname("registry.terraform.io")
|
|||
// FQN.
|
||||
const LegacyProviderNamespace = "-"
|
||||
|
||||
// String returns an FQN string, indended for use in output.
|
||||
// String returns an FQN string, indended for use in machine-readable output.
|
||||
func (pt Provider) String() string {
|
||||
if pt.IsZero() {
|
||||
panic("called String on zero-value addrs.Provider")
|
||||
|
@ -38,6 +52,19 @@ func (pt Provider) String() string {
|
|||
return pt.Hostname.ForDisplay() + "/" + pt.Namespace + "/" + pt.Type
|
||||
}
|
||||
|
||||
// ForDisplay returns a user-friendly FQN string, simplified for readability. If
|
||||
// the provider is using the default hostname, the hostname is omitted.
|
||||
func (pt Provider) ForDisplay() string {
|
||||
if pt.IsZero() {
|
||||
panic("called ForDisplay on zero-value addrs.Provider")
|
||||
}
|
||||
|
||||
if pt.Hostname == DefaultRegistryHost {
|
||||
return pt.Namespace + "/" + pt.Type
|
||||
}
|
||||
return pt.Hostname.ForDisplay() + "/" + pt.Namespace + "/" + pt.Type
|
||||
}
|
||||
|
||||
// NewProvider constructs a provider address from its parts, and normalizes
|
||||
// the namespace and type parts to lowercase using unicode case folding rules
|
||||
// so that resulting addrs.Provider values can be compared using standard
|
||||
|
@ -64,6 +91,30 @@ func NewProvider(hostname svchost.Hostname, namespace, typeName string) Provider
|
|||
}
|
||||
}
|
||||
|
||||
// ImpliedProviderForUnqualifiedType represents the rules for inferring what
|
||||
// provider FQN a user intended when only a naked type name is available.
|
||||
//
|
||||
// For all except the type name "terraform" this returns a so-called "default"
|
||||
// provider, which is under the registry.terraform.io/hashicorp/ namespace.
|
||||
//
|
||||
// As a special case, the string "terraform" maps to
|
||||
// "terraform.io/builtin/terraform" because that is the more likely user
|
||||
// intent than the now-unmaintained "registry.terraform.io/hashicorp/terraform"
|
||||
// which remains only for compatibility with older Terraform versions.
|
||||
func ImpliedProviderForUnqualifiedType(typeName string) Provider {
|
||||
switch typeName {
|
||||
case "terraform":
|
||||
// Note for future maintainers: any additional strings we add here
|
||||
// as implied to be builtin must never also be use as provider names
|
||||
// in the registry.terraform.io/hashicorp/... namespace, because
|
||||
// otherwise older versions of Terraform could implicitly select
|
||||
// the registry name instead of the internal one.
|
||||
return NewBuiltInProvider(typeName)
|
||||
default:
|
||||
return NewDefaultProvider(typeName)
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultProvider returns the default address of a HashiCorp-maintained,
|
||||
// Registry-hosted provider.
|
||||
func NewDefaultProvider(name string) Provider {
|
||||
|
@ -74,6 +125,16 @@ func NewDefaultProvider(name string) Provider {
|
|||
}
|
||||
}
|
||||
|
||||
// NewBuiltInProvider returns the address of a "built-in" provider. See
|
||||
// the docs for Provider.IsBuiltIn for more information.
|
||||
func NewBuiltInProvider(name string) Provider {
|
||||
return Provider{
|
||||
Type: MustParseProviderPart(name),
|
||||
Namespace: BuiltInProviderNamespace,
|
||||
Hostname: BuiltInProviderHost,
|
||||
}
|
||||
}
|
||||
|
||||
// NewLegacyProvider returns a mock address for a provider.
|
||||
// This will be removed when ProviderType is fully integrated.
|
||||
func NewLegacyProvider(name string) Provider {
|
||||
|
@ -109,6 +170,17 @@ func (pt Provider) IsZero() bool {
|
|||
return pt == Provider{}
|
||||
}
|
||||
|
||||
// IsBuiltIn returns true if the receiver is the address of a "built-in"
|
||||
// provider. That is, a provider under terraform.io/builtin/ which is
|
||||
// included as part of the Terraform binary itself rather than one to be
|
||||
// installed from elsewhere.
|
||||
//
|
||||
// These are ignored by the provider installer because they are assumed to
|
||||
// already be available without any further installation.
|
||||
func (pt Provider) IsBuiltIn() bool {
|
||||
return pt.Hostname == BuiltInProviderHost && pt.Namespace == BuiltInProviderNamespace
|
||||
}
|
||||
|
||||
// LessThan returns true if the receiver should sort before the other given
|
||||
// address in an ordered list of provider addresses.
|
||||
//
|
||||
|
|
|
@ -7,6 +7,84 @@ import (
|
|||
svchost "github.com/hashicorp/terraform-svchost"
|
||||
)
|
||||
|
||||
func TestProviderString(t *testing.T) {
|
||||
tests := []struct {
|
||||
Input Provider
|
||||
Want string
|
||||
}{
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: DefaultRegistryHost,
|
||||
Namespace: "hashicorp",
|
||||
},
|
||||
DefaultRegistryHost.ForDisplay() + "/hashicorp/test",
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: "registry.terraform.com",
|
||||
Namespace: "hashicorp",
|
||||
},
|
||||
"registry.terraform.com/hashicorp/test",
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: DefaultRegistryHost,
|
||||
Namespace: "othercorp",
|
||||
},
|
||||
DefaultRegistryHost.ForDisplay() + "/othercorp/test",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got := test.Input.String()
|
||||
if got != test.Want {
|
||||
t.Errorf("wrong result for %s\n", test.Input.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderDisplay(t *testing.T) {
|
||||
tests := []struct {
|
||||
Input Provider
|
||||
Want string
|
||||
}{
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: DefaultRegistryHost,
|
||||
Namespace: "hashicorp",
|
||||
},
|
||||
"hashicorp/test",
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: "registry.terraform.com",
|
||||
Namespace: "hashicorp",
|
||||
},
|
||||
"registry.terraform.com/hashicorp/test",
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: DefaultRegistryHost,
|
||||
Namespace: "othercorp",
|
||||
},
|
||||
"othercorp/test",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got := test.Input.ForDisplay()
|
||||
if got != test.Want {
|
||||
t.Errorf("wrong result for %s\n", test.Input.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderIsDefault(t *testing.T) {
|
||||
tests := []struct {
|
||||
Input Provider
|
||||
|
@ -46,6 +124,77 @@ func TestProviderIsDefault(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProviderIsBuiltIn(t *testing.T) {
|
||||
tests := []struct {
|
||||
Input Provider
|
||||
Want bool
|
||||
}{
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: BuiltInProviderHost,
|
||||
Namespace: BuiltInProviderNamespace,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "terraform",
|
||||
Hostname: BuiltInProviderHost,
|
||||
Namespace: BuiltInProviderNamespace,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: BuiltInProviderHost,
|
||||
Namespace: "boop",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: DefaultRegistryHost,
|
||||
Namespace: BuiltInProviderNamespace,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: DefaultRegistryHost,
|
||||
Namespace: "hashicorp",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: "registry.terraform.com",
|
||||
Namespace: "hashicorp",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
Provider{
|
||||
Type: "test",
|
||||
Hostname: DefaultRegistryHost,
|
||||
Namespace: "othercorp",
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got := test.Input.IsBuiltIn()
|
||||
if got != test.Want {
|
||||
t.Errorf("wrong result for %s\ngot: %#v\nwant: %#v", test.Input.String(), got, test.Want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderIsLegacy(t *testing.T) {
|
||||
tests := []struct {
|
||||
Input Provider
|
||||
|
|
|
@ -59,7 +59,7 @@ func TestLocal_applyBasic(t *testing.T) {
|
|||
checkState(t, b.StateOutPath, `
|
||||
test_instance.foo:
|
||||
ID = yes
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
ami = bar
|
||||
`)
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ func TestLocal_applyError(t *testing.T) {
|
|||
checkState(t, b.StateOutPath, `
|
||||
test_instance.foo:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
ami = bar
|
||||
`)
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ func TestLocal_applyBackendFail(t *testing.T) {
|
|||
checkState(t, "errored.tfstate", `
|
||||
test_instance.foo:
|
||||
ID = yes
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
ami = bar
|
||||
`)
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ func TestLocal_planDeposedOnly(t *testing.T) {
|
|||
}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -660,7 +660,7 @@ func testPlanState() *states.State {
|
|||
}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -687,7 +687,7 @@ func testPlanState_withDataSource() *states.State {
|
|||
}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -704,7 +704,7 @@ func testPlanState_withDataSource() *states.State {
|
|||
}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -731,7 +731,7 @@ func testPlanState_tainted() *states.State {
|
|||
}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -5,12 +5,14 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/initwd"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
|
@ -19,7 +21,7 @@ func TestLocal_refresh(t *testing.T) {
|
|||
defer cleanup()
|
||||
|
||||
p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
|
||||
terraform.TestStateFile(t, b.StatePath, testRefreshState())
|
||||
testStateFile(t, b.StatePath, testRefreshState())
|
||||
|
||||
p.ReadResourceFn = nil
|
||||
p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
|
||||
|
@ -42,7 +44,7 @@ func TestLocal_refresh(t *testing.T) {
|
|||
checkState(t, b.StateOutPath, `
|
||||
test_instance.foo:
|
||||
ID = yes
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
`)
|
||||
}
|
||||
|
||||
|
@ -50,7 +52,7 @@ func TestLocal_refreshNoConfig(t *testing.T) {
|
|||
b, cleanup := TestLocal(t)
|
||||
defer cleanup()
|
||||
p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
|
||||
terraform.TestStateFile(t, b.StatePath, testRefreshState())
|
||||
testStateFile(t, b.StatePath, testRefreshState())
|
||||
p.ReadResourceFn = nil
|
||||
p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("yes"),
|
||||
|
@ -72,7 +74,7 @@ func TestLocal_refreshNoConfig(t *testing.T) {
|
|||
checkState(t, b.StateOutPath, `
|
||||
test_instance.foo:
|
||||
ID = yes
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
`)
|
||||
}
|
||||
|
||||
|
@ -81,7 +83,7 @@ func TestLocal_refreshNilModuleWithInput(t *testing.T) {
|
|||
b, cleanup := TestLocal(t)
|
||||
defer cleanup()
|
||||
p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
|
||||
terraform.TestStateFile(t, b.StatePath, testRefreshState())
|
||||
testStateFile(t, b.StatePath, testRefreshState())
|
||||
p.ReadResourceFn = nil
|
||||
p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("yes"),
|
||||
|
@ -105,7 +107,7 @@ func TestLocal_refreshNilModuleWithInput(t *testing.T) {
|
|||
checkState(t, b.StateOutPath, `
|
||||
test_instance.foo:
|
||||
ID = yes
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
`)
|
||||
}
|
||||
|
||||
|
@ -113,7 +115,7 @@ func TestLocal_refreshInput(t *testing.T) {
|
|||
b, cleanup := TestLocal(t)
|
||||
defer cleanup()
|
||||
p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
|
||||
terraform.TestStateFile(t, b.StatePath, testRefreshState())
|
||||
testStateFile(t, b.StatePath, testRefreshState())
|
||||
|
||||
p.GetSchemaReturn = &terraform.ProviderSchema{
|
||||
Provider: &configschema.Block{
|
||||
|
@ -163,7 +165,7 @@ func TestLocal_refreshInput(t *testing.T) {
|
|||
checkState(t, b.StateOutPath, `
|
||||
test_instance.foo:
|
||||
ID = yes
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
`)
|
||||
}
|
||||
|
||||
|
@ -171,7 +173,7 @@ func TestLocal_refreshValidate(t *testing.T) {
|
|||
b, cleanup := TestLocal(t)
|
||||
defer cleanup()
|
||||
p := TestLocalProvider(t, b, "test", refreshFixtureSchema())
|
||||
terraform.TestStateFile(t, b.StatePath, testRefreshState())
|
||||
testStateFile(t, b.StatePath, testRefreshState())
|
||||
p.ReadResourceFn = nil
|
||||
p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("yes"),
|
||||
|
@ -196,7 +198,7 @@ func TestLocal_refreshValidate(t *testing.T) {
|
|||
checkState(t, b.StateOutPath, `
|
||||
test_instance.foo:
|
||||
ID = yes
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
`)
|
||||
}
|
||||
|
||||
|
@ -213,24 +215,18 @@ func testOperationRefresh(t *testing.T, configDir string) (*backend.Operation, f
|
|||
}
|
||||
|
||||
// testRefreshState is just a common state that we use for testing refresh.
|
||||
func testRefreshState() *terraform.State {
|
||||
return &terraform.State{
|
||||
Version: 2,
|
||||
Modules: []*terraform.ModuleState{
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root"},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_instance.foo": &terraform.ResourceState{
|
||||
Type: "test_instance",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: map[string]*terraform.OutputState{},
|
||||
},
|
||||
func testRefreshState() *states.State {
|
||||
state := states.NewState()
|
||||
root := state.EnsureModule(addrs.RootModuleInstance)
|
||||
root.SetResourceInstanceCurrent(
|
||||
mustResourceInstanceAddr("test_instance.foo").Resource,
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
}
|
||||
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
||||
)
|
||||
return state
|
||||
}
|
||||
|
||||
// refreshFixtureSchema returns a schema suitable for processing the
|
||||
|
|
|
@ -112,11 +112,9 @@ func TestLocalProvider(t *testing.T, b *Local, name string, schema *terraform.Pr
|
|||
}
|
||||
|
||||
// Setup our provider
|
||||
b.ContextOpts.ProviderResolver = providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider(name): providers.FactoryFixed(p),
|
||||
},
|
||||
)
|
||||
b.ContextOpts.Providers = map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider(name): providers.FactoryFixed(p),
|
||||
}
|
||||
|
||||
return p
|
||||
|
||||
|
@ -211,3 +209,19 @@ func testStateFile(t *testing.T, path string, s *states.State) {
|
|||
stateFile := statemgr.NewFilesystem(path)
|
||||
stateFile.WriteState(s)
|
||||
}
|
||||
|
||||
func mustProviderConfig(s string) addrs.AbsProviderConfig {
|
||||
p, diags := addrs.ParseAbsProviderConfigStr(s)
|
||||
if diags.HasErrors() {
|
||||
panic(diags.Err())
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func mustResourceInstanceAddr(s string) addrs.AbsResourceInstance {
|
||||
addr, diags := addrs.ParseAbsResourceInstanceStr(s)
|
||||
if diags.HasErrors() {
|
||||
panic(diags.Err())
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ func TestApply_destroy(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -126,7 +126,7 @@ func TestApply_destroyLockedState(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -201,7 +201,7 @@ func TestApply_destroyTargeted(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -217,7 +217,7 @@ func TestApply_destroyTargeted(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -203,10 +203,10 @@ func TestApply_parallelism(t *testing.T) {
|
|||
NewState: cty.EmptyObjectVal,
|
||||
}
|
||||
}
|
||||
providerFactories[addrs.NewLegacyProvider(name)] = providers.FactoryFixed(provider)
|
||||
providerFactories[addrs.NewDefaultProvider(name)] = providers.FactoryFixed(provider)
|
||||
}
|
||||
testingOverrides := &testingOverrides{
|
||||
ProviderResolver: providers.ResolverFixed(providerFactories),
|
||||
Providers: providerFactories,
|
||||
}
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
|
@ -834,7 +834,7 @@ func TestApply_refresh(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -991,7 +991,7 @@ func TestApply_state(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -1358,7 +1358,7 @@ func TestApply_backup(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -1662,7 +1662,7 @@ func applyFixturePlanFile(t *testing.T) string {
|
|||
Name: "foo",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
ProviderAddr: addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
ChangeSrc: plans.ChangeSrc{
|
||||
|
|
|
@ -119,21 +119,17 @@ func testFixturePath(name string) string {
|
|||
|
||||
func metaOverridesForProvider(p providers.Interface) *testingOverrides {
|
||||
return &testingOverrides{
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("test"): providers.FactoryFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): providers.FactoryFixed(p),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func metaOverridesForProviderAndProvisioner(p providers.Interface, pr provisioners.Interface) *testingOverrides {
|
||||
return &testingOverrides{
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("test"): providers.FactoryFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): providers.FactoryFixed(p),
|
||||
},
|
||||
Provisioners: map[string]provisioners.Factory{
|
||||
"shell": provisioners.FactoryFixed(pr),
|
||||
},
|
||||
|
@ -272,7 +268,7 @@ func testState() *states.State {
|
|||
DependsOn: []addrs.Referenceable{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -705,7 +701,7 @@ func testInputMap(t *testing.T, answers map[string]string) func() {
|
|||
// be returned about the backend configuration having changed and that
|
||||
// "terraform init" must be run, since the test backend config cache created
|
||||
// by this function contains the hash for an empty configuration.
|
||||
func testBackendState(t *testing.T, s *terraform.State, c int) (*terraform.State, *httptest.Server) {
|
||||
func testBackendState(t *testing.T, s *states.State, c int) (*terraform.State, *httptest.Server) {
|
||||
t.Helper()
|
||||
|
||||
var b64md5 string
|
||||
|
@ -727,8 +723,8 @@ func testBackendState(t *testing.T, s *terraform.State, c int) (*terraform.State
|
|||
|
||||
// If a state was given, make sure we calculate the proper b64md5
|
||||
if s != nil {
|
||||
enc := json.NewEncoder(buf)
|
||||
if err := enc.Encode(s); err != nil {
|
||||
err := statefile.Write(&statefile.File{State: s}, buf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
md5 := md5.Sum(buf.Bytes())
|
||||
|
|
|
@ -40,11 +40,11 @@ func TestPlanApplyInAutomation(t *testing.T) {
|
|||
|
||||
// Make sure we actually downloaded the plugins, rather than picking up
|
||||
// copies that might be already installed globally on the system.
|
||||
if !strings.Contains(stdout, "- Downloading plugin for provider \"template") {
|
||||
if !strings.Contains(stdout, "Installing hashicorp/template v") {
|
||||
t.Errorf("template provider download message is missing from init output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
if !strings.Contains(stdout, "- Downloading plugin for provider \"null") {
|
||||
if !strings.Contains(stdout, "Installing hashicorp/null v") {
|
||||
t.Errorf("null provider download message is missing from init output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
|
@ -135,11 +135,11 @@ func TestAutoApplyInAutomation(t *testing.T) {
|
|||
|
||||
// Make sure we actually downloaded the plugins, rather than picking up
|
||||
// copies that might be already installed globally on the system.
|
||||
if !strings.Contains(stdout, "- Downloading plugin for provider \"template") {
|
||||
if !strings.Contains(stdout, "Installing hashicorp/template v") {
|
||||
t.Errorf("template provider download message is missing from init output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
if !strings.Contains(stdout, "- Downloading plugin for provider \"null") {
|
||||
if !strings.Contains(stdout, "Installing hashicorp/null v") {
|
||||
t.Errorf("null provider download message is missing from init output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
|
@ -202,11 +202,11 @@ func TestPlanOnlyInAutomation(t *testing.T) {
|
|||
|
||||
// Make sure we actually downloaded the plugins, rather than picking up
|
||||
// copies that might be already installed globally on the system.
|
||||
if !strings.Contains(stdout, "- Downloading plugin for provider \"template") {
|
||||
if !strings.Contains(stdout, "Installing hashicorp/template v") {
|
||||
t.Errorf("template provider download message is missing from init output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
if !strings.Contains(stdout, "- Downloading plugin for provider \"null") {
|
||||
if !strings.Contains(stdout, "Installing hashicorp/null v") {
|
||||
t.Errorf("null provider download message is missing from init output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
|
|
|
@ -39,12 +39,12 @@ func TestInitProviders(t *testing.T) {
|
|||
t.Errorf("success message is missing from output:\n%s", stdout)
|
||||
}
|
||||
|
||||
if !strings.Contains(stdout, "- Downloading plugin for provider \"template\" (hashicorp/template)") {
|
||||
if !strings.Contains(stdout, "- Installing hashicorp/template v") {
|
||||
t.Errorf("provider download message is missing from output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
|
||||
if !strings.Contains(stdout, "* provider.template: version = ") {
|
||||
if !strings.Contains(stdout, "* hashicorp/template: version = ") {
|
||||
t.Errorf("provider pinning recommendation is missing from output:\n%s", stdout)
|
||||
}
|
||||
|
||||
|
@ -73,12 +73,61 @@ func TestInitProvidersInternal(t *testing.T) {
|
|||
t.Errorf("success message is missing from output:\n%s", stdout)
|
||||
}
|
||||
|
||||
if strings.Contains(stdout, "Downloading plugin for provider") {
|
||||
if strings.Contains(stdout, "Installing hashicorp/terraform") {
|
||||
// Shouldn't have downloaded anything with this config, because the
|
||||
// provider is built in.
|
||||
t.Errorf("provider download message appeared in output:\n%s", stdout)
|
||||
}
|
||||
|
||||
if strings.Contains(stdout, "Installing terraform.io/builtin/terraform") {
|
||||
// Shouldn't have downloaded anything with this config, because the
|
||||
// provider is built in.
|
||||
t.Errorf("provider download message appeared in output:\n%s", stdout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitProvidersVendored(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// This test will try to reach out to registry.terraform.io as one of the
|
||||
// possible installation locations for
|
||||
// hashicorp/null, where it will find that
|
||||
// versions do exist but will ultimately select the version that is
|
||||
// vendored due to the version constraint.
|
||||
skipIfCannotAccessNetwork(t)
|
||||
|
||||
fixturePath := filepath.Join("testdata", "vendored-provider")
|
||||
tf := e2e.NewBinary(terraformBin, fixturePath)
|
||||
defer tf.Close()
|
||||
|
||||
// Our fixture dir has a generic os_arch dir, which we need to customize
|
||||
// to the actual OS/arch where this test is running in order to get the
|
||||
// desired result.
|
||||
fixtMachineDir := tf.Path("terraform.d/plugins/registry.terraform.io/hashicorp/null/1.0.0+local/os_arch")
|
||||
wantMachineDir := tf.Path("terraform.d/plugins/registry.terraform.io/hashicorp/null/1.0.0+local/", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
|
||||
err := os.Rename(fixtMachineDir, wantMachineDir)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
stdout, stderr, err := tf.Run("init")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if stderr != "" {
|
||||
t.Errorf("unexpected stderr output:\n%s", stderr)
|
||||
}
|
||||
|
||||
if !strings.Contains(stdout, "Terraform has been successfully initialized!") {
|
||||
t.Errorf("success message is missing from output:\n%s", stdout)
|
||||
}
|
||||
|
||||
if !strings.Contains(stdout, "- Installing hashicorp/null v1.0.0+local") {
|
||||
t.Errorf("provider download message is missing from output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestInitProviders_pluginCache(t *testing.T) {
|
||||
|
@ -96,16 +145,19 @@ func TestInitProviders_pluginCache(t *testing.T) {
|
|||
// Our fixture dir has a generic os_arch dir, which we need to customize
|
||||
// to the actual OS/arch where this test is running in order to get the
|
||||
// desired result.
|
||||
fixtMachineDir := tf.Path("cache/os_arch")
|
||||
wantMachineDir := tf.Path("cache", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
|
||||
os.Rename(fixtMachineDir, wantMachineDir)
|
||||
fixtMachineDir := tf.Path("cache/registry.terraform.io/hashicorp/template/2.1.0/os_arch")
|
||||
wantMachineDir := tf.Path("cache/registry.terraform.io/hashicorp/template/2.1.0/", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
|
||||
err := os.Rename(fixtMachineDir, wantMachineDir)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
cmd := tf.Cmd("init")
|
||||
cmd.Env = append(cmd.Env, "TF_PLUGIN_CACHE_DIR=./cache")
|
||||
cmd.Stdin = nil
|
||||
cmd.Stderr = &bytes.Buffer{}
|
||||
|
||||
err := cmd.Run()
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
@ -115,7 +167,7 @@ func TestInitProviders_pluginCache(t *testing.T) {
|
|||
t.Errorf("unexpected stderr output:\n%s\n", stderr)
|
||||
}
|
||||
|
||||
path := fmt.Sprintf(".terraform/plugins/%s_%s/terraform-provider-template_v2.1.0_x4", runtime.GOOS, runtime.GOARCH)
|
||||
path := fmt.Sprintf(".terraform/plugins/registry.terraform.io/hashicorp/template/2.1.0/%s_%s/terraform-provider-template_v2.1.0_x4", runtime.GOOS, runtime.GOARCH)
|
||||
content, err := tf.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read installed plugin from %s: %s", path, err)
|
||||
|
@ -124,11 +176,11 @@ func TestInitProviders_pluginCache(t *testing.T) {
|
|||
t.Errorf("template plugin was not installed from local cache")
|
||||
}
|
||||
|
||||
if !tf.FileExists(fmt.Sprintf(".terraform/plugins/%s_%s/terraform-provider-null_v2.1.0_x4", runtime.GOOS, runtime.GOARCH)) {
|
||||
if !tf.FileExists(fmt.Sprintf(".terraform/plugins/registry.terraform.io/hashicorp/null/2.1.0/%s_%s/terraform-provider-null_v2.1.0_x4", runtime.GOOS, runtime.GOARCH)) {
|
||||
t.Errorf("null plugin was not installed")
|
||||
}
|
||||
|
||||
if !tf.FileExists(fmt.Sprintf("cache/%s_%s/terraform-provider-null_v2.1.0_x4", runtime.GOOS, runtime.GOARCH)) {
|
||||
if !tf.FileExists(fmt.Sprintf("cache/registry.terraform.io/hashicorp/null/2.1.0/%s_%s/terraform-provider-null_v2.1.0_x4", runtime.GOOS, runtime.GOARCH)) {
|
||||
t.Errorf("null plugin is not in cache after install")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,11 +38,11 @@ func TestPrimarySeparatePlan(t *testing.T) {
|
|||
|
||||
// Make sure we actually downloaded the plugins, rather than picking up
|
||||
// copies that might be already installed globally on the system.
|
||||
if !strings.Contains(stdout, "- Downloading plugin for provider \"template") {
|
||||
if !strings.Contains(stdout, "Installing hashicorp/template v") {
|
||||
t.Errorf("template provider download message is missing from init output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
if !strings.Contains(stdout, "- Downloading plugin for provider \"null") {
|
||||
if !strings.Contains(stdout, "Installing hashicorp/null v") {
|
||||
t.Errorf("null provider download message is missing from init output:\n%s", stdout)
|
||||
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
null = {
|
||||
source = "hashicorp/null"
|
||||
version = "1.0.0+local"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -88,7 +88,7 @@ func TestVersionWithProvider(t *testing.T) {
|
|||
t.Errorf("unexpected stderr output:\n%s", stderr)
|
||||
}
|
||||
|
||||
wantMsg := "+ provider.template v" // we don't know which version we'll get here
|
||||
wantMsg := "+ provider registry.terraform.io/hashicorp/template v" // we don't know which version we'll get here
|
||||
if !strings.Contains(stdout, wantMsg) {
|
||||
t.Errorf("output does not contain provider information %q:\n%s", wantMsg, stdout)
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ func formatStateModule(p blockBodyDiffPrinter, m *states.Module, schemas *terraf
|
|||
// loaded all of the schemas and checked things prior to this
|
||||
// point. We can't return errors here, but since this is UI code
|
||||
// we will try to do _something_ reasonable.
|
||||
p.buf.WriteString(fmt.Sprintf("# missing schema for provider %q\n\n", provider.LegacyString()))
|
||||
p.buf.WriteString(fmt.Sprintf("# missing schema for provider %q\n\n", provider.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ func TestGraph(t *testing.T) {
|
|||
}
|
||||
|
||||
output := ui.OutputWriter.String()
|
||||
if !strings.Contains(output, `provider["registry.terraform.io/-/test"]`) {
|
||||
if !strings.Contains(output, `provider["registry.terraform.io/hashicorp/test"]`) {
|
||||
t.Fatalf("doesn't look like digraph: %s", output)
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ func TestGraph_noArgs(t *testing.T) {
|
|||
}
|
||||
|
||||
output := ui.OutputWriter.String()
|
||||
if !strings.Contains(output, `provider["registry.terraform.io/-/test"]`) {
|
||||
if !strings.Contains(output, `provider["registry.terraform.io/hashicorp/test"]`) {
|
||||
t.Fatalf("doesn't look like digraph: %s", output)
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ func TestGraph_plan(t *testing.T) {
|
|||
}
|
||||
|
||||
output := ui.OutputWriter.String()
|
||||
if !strings.Contains(output, `provider["registry.terraform.io/-/test"]`) {
|
||||
if !strings.Contains(output, `provider["registry.terraform.io/hashicorp/test"]`) {
|
||||
t.Fatalf("doesn't look like digraph: %s", output)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package command
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -14,7 +13,6 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/helper/copy"
|
||||
"github.com/hashicorp/terraform/plugin/discovery"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
|
@ -160,22 +158,21 @@ func TestImport_remoteState(t *testing.T) {
|
|||
|
||||
statePath := "imported.tfstate"
|
||||
|
||||
providerSource, close := newMockProviderSource(t, map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
})
|
||||
defer close()
|
||||
|
||||
// init our backend
|
||||
ui := cli.NewMockUi()
|
||||
m := Meta{
|
||||
testingOverrides: metaOverridesForProvider(testProvider()),
|
||||
Ui: ui,
|
||||
ProviderSource: providerSource,
|
||||
}
|
||||
|
||||
ic := &InitCommand{
|
||||
Meta: m,
|
||||
providerInstaller: &mockProviderInstaller{
|
||||
Providers: map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
},
|
||||
|
||||
Dir: m.pluginDir(),
|
||||
},
|
||||
}
|
||||
|
||||
// (Using log here rather than t.Log so that these messages interleave with other trace logs)
|
||||
|
@ -267,22 +264,21 @@ func TestImport_initializationErrorShouldUnlock(t *testing.T) {
|
|||
|
||||
statePath := "imported.tfstate"
|
||||
|
||||
providerSource, close := newMockProviderSource(t, map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
})
|
||||
defer close()
|
||||
|
||||
// init our backend
|
||||
ui := cli.NewMockUi()
|
||||
m := Meta{
|
||||
testingOverrides: metaOverridesForProvider(testProvider()),
|
||||
Ui: ui,
|
||||
ProviderSource: providerSource,
|
||||
}
|
||||
|
||||
ic := &InitCommand{
|
||||
Meta: m,
|
||||
providerInstaller: &mockProviderInstaller{
|
||||
Providers: map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
},
|
||||
|
||||
Dir: m.pluginDir(),
|
||||
},
|
||||
}
|
||||
|
||||
// (Using log here rather than t.Log so that these messages interleave with other trace logs)
|
||||
|
@ -317,7 +313,7 @@ func TestImport_initializationErrorShouldUnlock(t *testing.T) {
|
|||
|
||||
// specifically, it should fail due to a missing provider
|
||||
msg := ui.ErrorWriter.String()
|
||||
if want := "Could not satisfy plugin requirements"; !strings.Contains(msg, want) {
|
||||
if want := `unknown provider "registry.terraform.io/hashicorp/unknown"`; !strings.Contains(msg, want) {
|
||||
t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg)
|
||||
}
|
||||
|
||||
|
@ -838,87 +834,20 @@ func TestImport_targetIsModule(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// make sure we search the full plugin path during import
|
||||
func TestImport_pluginDir(t *testing.T) {
|
||||
td := tempDir(t)
|
||||
copy.CopyDir(testFixturePath("import-provider"), td)
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
// make a fake provider in a custom plugin directory
|
||||
if err := os.Mkdir("plugins", 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile("plugins/terraform-provider-test_v1.1.1_x4", []byte("invalid binary"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &ImportCommand{
|
||||
Meta: Meta{
|
||||
Ui: ui,
|
||||
},
|
||||
}
|
||||
|
||||
// store our custom plugin path, which would normally happen during init
|
||||
if err := c.storePluginPath([]string{"./plugins"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now we need to go through some plugin init.
|
||||
// This discovers our fake plugin and writes the lock file.
|
||||
initUi := new(cli.MockUi)
|
||||
initCmd := &InitCommand{
|
||||
Meta: Meta{
|
||||
pluginPath: []string{"./plugins"},
|
||||
Ui: initUi,
|
||||
},
|
||||
providerInstaller: &discovery.ProviderInstaller{
|
||||
PluginProtocolVersion: discovery.PluginInstallProtocolVersion,
|
||||
},
|
||||
}
|
||||
if code := initCmd.Run(nil); code != 0 {
|
||||
t.Fatal(initUi.ErrorWriter.String())
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"test_instance.foo",
|
||||
"bar",
|
||||
}
|
||||
if code := c.Run(args); code == 0 {
|
||||
t.Fatalf("expected error, got: %s", ui.OutputWriter)
|
||||
}
|
||||
|
||||
outMsg := ui.OutputWriter.String()
|
||||
// if we were missing a plugin, the output will have some explanation
|
||||
// about requirements. If discovery starts verifying binary compatibility,
|
||||
// we will need to write a dummy provider above.
|
||||
if strings.Contains(outMsg, "requirements") {
|
||||
t.Fatal("unexpected output:", outMsg)
|
||||
}
|
||||
|
||||
// We wanted a plugin execution error, rather than a requirement error.
|
||||
// Looking for "exec" in the error should suffice for now.
|
||||
errMsg := ui.ErrorWriter.String()
|
||||
if !strings.Contains(errMsg, "exec") {
|
||||
t.Fatal("unexpected error:", errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
const testImportStr = `
|
||||
test_instance.foo:
|
||||
ID = yay
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
`
|
||||
|
||||
const testImportCustomProviderStr = `
|
||||
test_instance.foo:
|
||||
ID = yay
|
||||
provider = provider["registry.terraform.io/-/test"].alias
|
||||
provider = provider["registry.terraform.io/hashicorp/test"].alias
|
||||
`
|
||||
|
||||
const testImportProviderMismatchStr = `
|
||||
test_instance.foo:
|
||||
ID = yay
|
||||
provider = provider["registry.terraform.io/-/test-beta"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test-beta"]
|
||||
`
|
||||
|
|
283
command/init.go
283
command/init.go
|
@ -1,8 +1,8 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -12,19 +12,17 @@ import (
|
|||
"github.com/posener/complete"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
backendInit "github.com/hashicorp/terraform/backend/init"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/earlyconfig"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
"github.com/hashicorp/terraform/internal/initwd"
|
||||
"github.com/hashicorp/terraform/plugin/discovery"
|
||||
"github.com/hashicorp/terraform/internal/providercache"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/hashicorp/terraform/version"
|
||||
)
|
||||
|
||||
// InitCommand is a Command implementation that takes a Terraform
|
||||
|
@ -34,12 +32,6 @@ type InitCommand struct {
|
|||
|
||||
// getPlugins is for the -get-plugins flag
|
||||
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 {
|
||||
|
@ -75,18 +67,6 @@ func (c *InitCommand) Run(args []string) int {
|
|||
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
|
||||
args = cmdFlags.Args()
|
||||
if len(args) > 1 {
|
||||
|
@ -312,7 +292,7 @@ func (c *InitCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// Now that we have loaded all modules, check the module tree for missing providers.
|
||||
providersOutput, providerDiags := c.getProviders(earlyConfig, state, flagUpgrade)
|
||||
providersOutput, providerDiags := c.getProviders(earlyConfig, state, flagUpgrade, flagPluginPath)
|
||||
diags = diags.Append(providerDiags)
|
||||
if providerDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
|
@ -443,176 +423,120 @@ the backend configuration is present and valid.
|
|||
|
||||
// Load the complete module tree, and fetch any missing providers.
|
||||
// This method outputs its own Ui.
|
||||
func (c *InitCommand) getProviders(earlyConfig *earlyconfig.Config, state *states.State, upgrade bool) (output bool, diags tfdiags.Diagnostics) {
|
||||
var available discovery.PluginMetaSet
|
||||
if upgrade {
|
||||
// If we're in upgrade mode, we ignore any auto-installed plugins
|
||||
// in "available", causing us to reinstall and possibly upgrade them.
|
||||
available = c.providerPluginManuallyInstalledSet()
|
||||
} else {
|
||||
available = c.providerPluginSet()
|
||||
}
|
||||
|
||||
configDeps, depsDiags := earlyConfig.ProviderDependencies()
|
||||
diags = diags.Append(depsDiags)
|
||||
if depsDiags.HasErrors() {
|
||||
func (c *InitCommand) getProviders(earlyConfig *earlyconfig.Config, state *states.State, upgrade bool, pluginDirs []string) (output bool, diags tfdiags.Diagnostics) {
|
||||
// First we'll collect all the provider dependencies we can see in the
|
||||
// configuration and the state.
|
||||
reqs, moreDiags := earlyConfig.ProviderRequirements()
|
||||
diags = diags.Append(moreDiags)
|
||||
if moreDiags.HasErrors() {
|
||||
return false, diags
|
||||
}
|
||||
|
||||
configReqs := configDeps.AllProviderRequirements()
|
||||
// FIXME: This is weird because ConfigTreeDependencies was written before
|
||||
// we switched over to using earlyConfig as the main source of dependencies.
|
||||
// In future we should clean this up to be a more reasonable API.
|
||||
stateReqs := terraform.ConfigTreeDependencies(nil, state).AllProviderRequirements()
|
||||
|
||||
requirements := configReqs.Merge(stateReqs)
|
||||
if len(requirements) == 0 {
|
||||
// nothing to initialize
|
||||
return false, nil
|
||||
if state != nil {
|
||||
stateReqs := state.ProviderRequirements()
|
||||
reqs = reqs.Merge(stateReqs)
|
||||
}
|
||||
|
||||
c.Ui.Output(c.Colorize().Color(
|
||||
"\n[reset][bold]Initializing provider plugins...",
|
||||
))
|
||||
var inst *providercache.Installer
|
||||
if len(pluginDirs) == 0 {
|
||||
// By default we use a source that looks for providers in all of the
|
||||
// standard locations, possibly customized by the user in CLI config.
|
||||
inst = c.providerInstaller()
|
||||
} else {
|
||||
// If the user passes at least one -plugin-dir then that circumvents
|
||||
// the usual sources and forces Terraform to consult only the given
|
||||
// directories. Anything not available in one of those directories
|
||||
// is not available for installation.
|
||||
source := c.providerCustomLocalDirectorySource(pluginDirs)
|
||||
inst = c.providerInstallerCustomSource(source)
|
||||
}
|
||||
|
||||
missing := c.missingProviders(available, requirements)
|
||||
|
||||
if c.getPlugins {
|
||||
if len(missing) > 0 {
|
||||
c.Ui.Output("- Checking for available provider plugins...")
|
||||
}
|
||||
|
||||
for provider, reqd := range missing {
|
||||
pty := addrs.NewLegacyProvider(provider)
|
||||
_, providerDiags, err := c.providerInstaller.Get(pty, reqd.Versions)
|
||||
diags = diags.Append(providerDiags)
|
||||
|
||||
if err != nil {
|
||||
constraint := reqd.Versions.String()
|
||||
if constraint == "" {
|
||||
constraint = "(any version)"
|
||||
}
|
||||
|
||||
switch {
|
||||
case err == discovery.ErrorServiceUnreachable, err == discovery.ErrorPublicRegistryUnreachable:
|
||||
c.Ui.Error(errDiscoveryServiceUnreachable)
|
||||
case err == discovery.ErrorNoSuchProvider:
|
||||
c.Ui.Error(fmt.Sprintf(errProviderNotFound, provider, DefaultPluginVendorDir))
|
||||
case err == discovery.ErrorNoSuitableVersion:
|
||||
if reqd.Versions.Unconstrained() {
|
||||
// This should never happen, but might crop up if we catch
|
||||
// the releases server in a weird state where the provider's
|
||||
// directory is present but does not yet contain any
|
||||
// versions. We'll treat it like ErrorNoSuchProvider, then.
|
||||
c.Ui.Error(fmt.Sprintf(errProviderNotFound, provider, DefaultPluginVendorDir))
|
||||
} else {
|
||||
c.Ui.Error(fmt.Sprintf(errProviderVersionsUnsuitable, provider, reqd.Versions))
|
||||
}
|
||||
case errwrap.Contains(err, discovery.ErrorVersionIncompatible.Error()):
|
||||
// Attempt to fetch nested error to display to the user which versions
|
||||
// we considered and which versions might be compatible. Otherwise,
|
||||
// we'll just display a generic version incompatible msg
|
||||
incompatErr := errwrap.GetType(err, fmt.Errorf(""))
|
||||
if incompatErr != nil {
|
||||
c.Ui.Error(incompatErr.Error())
|
||||
} else {
|
||||
// Generic version incompatible msg
|
||||
c.Ui.Error(fmt.Sprintf(errProviderIncompatible, provider, constraint))
|
||||
}
|
||||
// Reset nested errors
|
||||
err = discovery.ErrorVersionIncompatible
|
||||
case err == discovery.ErrorNoVersionCompatible:
|
||||
// Generic version incompatible msg
|
||||
c.Ui.Error(fmt.Sprintf(errProviderIncompatible, provider, constraint))
|
||||
case err == discovery.ErrorSignatureVerification:
|
||||
c.Ui.Error(fmt.Sprintf(errSignatureVerification, provider, version.SemVer))
|
||||
case err == discovery.ErrorChecksumVerification,
|
||||
err == discovery.ErrorMissingChecksumVerification:
|
||||
c.Ui.Error(fmt.Sprintf(errChecksumVerification, provider))
|
||||
default:
|
||||
c.Ui.Error(fmt.Sprintf(errProviderInstallError, provider, err.Error(), DefaultPluginVendorDir))
|
||||
}
|
||||
|
||||
diags = diags.Append(err)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasErrors() {
|
||||
return true, diags
|
||||
}
|
||||
} else if len(missing) > 0 {
|
||||
// we have missing providers, but aren't going to try and download them
|
||||
var lines []string
|
||||
for provider, reqd := range missing {
|
||||
if reqd.Versions.Unconstrained() {
|
||||
lines = append(lines, fmt.Sprintf("* %s (any version)\n", provider))
|
||||
// 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
|
||||
// things relatively concise. Later it'd be nice to have a progress UI
|
||||
// where statuses update in-place, but we can't do that as long as we
|
||||
// are shimming our vt100 output to the legacy console API on Windows.
|
||||
evts := &providercache.InstallerEvents{
|
||||
PendingProviders: func(reqs map[addrs.Provider]getproviders.VersionConstraints) {
|
||||
c.Ui.Output(c.Colorize().Color(
|
||||
"\n[reset][bold]Initializing provider plugins...",
|
||||
))
|
||||
},
|
||||
ProviderAlreadyInstalled: func(provider addrs.Provider, selectedVersion getproviders.Version) {
|
||||
c.Ui.Info(fmt.Sprintf("- Using previously-installed %s v%s", provider.ForDisplay(), selectedVersion))
|
||||
},
|
||||
BuiltInProviderAvailable: func(provider addrs.Provider) {
|
||||
c.Ui.Info(fmt.Sprintf("- %s is built in to Terraform", provider.ForDisplay()))
|
||||
},
|
||||
BuiltInProviderFailure: func(provider addrs.Provider, err error) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid dependency on built-in provider",
|
||||
fmt.Sprintf("Cannot use %s: %s.", provider.ForDisplay(), err),
|
||||
))
|
||||
},
|
||||
QueryPackagesBegin: func(provider addrs.Provider, versionConstraints getproviders.VersionConstraints) {
|
||||
if len(versionConstraints) > 0 {
|
||||
c.Ui.Info(fmt.Sprintf("- Finding %s versions matching %q...", provider.ForDisplay(), getproviders.VersionConstraintsString(versionConstraints)))
|
||||
} else {
|
||||
lines = append(lines, fmt.Sprintf("* %s (%s)\n", provider, reqd.Versions))
|
||||
c.Ui.Info(fmt.Sprintf("- Finding latest version of %s...", provider.ForDisplay()))
|
||||
}
|
||||
diags = diags.Append(fmt.Errorf("missing provider %q", provider))
|
||||
}
|
||||
sort.Strings(lines)
|
||||
c.Ui.Error(fmt.Sprintf(errMissingProvidersNoInstall, strings.Join(lines, ""), DefaultPluginVendorDir))
|
||||
return true, diags
|
||||
},
|
||||
LinkFromCacheBegin: func(provider addrs.Provider, version getproviders.Version, cacheRoot string) {
|
||||
c.Ui.Info(fmt.Sprintf("- Using %s v%s from the shared cache directory", provider.ForDisplay(), version))
|
||||
},
|
||||
FetchPackageBegin: func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation) {
|
||||
c.Ui.Info(fmt.Sprintf("- Installing %s v%s...", provider.ForDisplay(), version))
|
||||
},
|
||||
QueryPackagesFailure: func(provider addrs.Provider, err error) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to query available provider packages",
|
||||
fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s.", provider.ForDisplay(), err),
|
||||
))
|
||||
},
|
||||
LinkFromCacheFailure: func(provider addrs.Provider, version getproviders.Version, err error) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to install provider from shared cache",
|
||||
fmt.Sprintf("Error while importing %s v%s from the shared cache directory: %s.", provider.ForDisplay(), version, err),
|
||||
))
|
||||
},
|
||||
FetchPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to install provider",
|
||||
fmt.Sprintf("Error while installing %s v%s: %s.", provider.ForDisplay(), version, err),
|
||||
))
|
||||
},
|
||||
}
|
||||
|
||||
// With all the providers downloaded, we'll generate our lock file
|
||||
// that ensures the provider binaries remain unchanged until we init
|
||||
// again. If anything changes, other commands that use providers will
|
||||
// fail with an error instructing the user to re-run this command.
|
||||
available = c.providerPluginSet() // re-discover to see newly-installed plugins
|
||||
|
||||
// internal providers were already filtered out, since we don't need to get them.
|
||||
chosen := chooseProviders(available, nil, requirements)
|
||||
|
||||
digests := map[string][]byte{}
|
||||
for name, meta := range chosen {
|
||||
digest, err := meta.SHA256()
|
||||
if err != nil {
|
||||
diags = diags.Append(fmt.Errorf("Failed to read provider plugin %s: %s", meta.Path, err))
|
||||
return true, diags
|
||||
}
|
||||
digests[name] = digest
|
||||
if c.ignorePluginChecksum {
|
||||
digests[name] = nil
|
||||
}
|
||||
mode := providercache.InstallNewProvidersOnly
|
||||
if upgrade {
|
||||
mode = providercache.InstallUpgrades
|
||||
}
|
||||
err := c.providerPluginsLock().Write(digests)
|
||||
// TODO: Use a context that will be cancelled when the Terraform
|
||||
// process receives SIGINT.
|
||||
ctx := evts.OnContext(context.TODO())
|
||||
selected, err := inst.EnsureProviderVersions(ctx, reqs, mode)
|
||||
if err != nil {
|
||||
diags = diags.Append(fmt.Errorf("failed to save provider manifest: %s", err))
|
||||
// The errors captured in "err" should be redundant with what we
|
||||
// received via the InstallerEvents callbacks above, so we'll
|
||||
// just return those as long as we have some.
|
||||
if !diags.HasErrors() {
|
||||
diags = diags.Append(err)
|
||||
}
|
||||
return true, diags
|
||||
}
|
||||
|
||||
{
|
||||
// Purge any auto-installed plugins that aren't being used.
|
||||
purged, err := c.providerInstaller.PurgeUnused(chosen)
|
||||
if err != nil {
|
||||
// Failure to purge old plugins is not a fatal error
|
||||
c.Ui.Warn(fmt.Sprintf("failed to purge unused plugins: %s", err))
|
||||
}
|
||||
if purged != nil {
|
||||
for meta := range purged {
|
||||
log.Printf("[DEBUG] Purged unused %s plugin %s", meta.Name, meta.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If any providers have "floating" versions (completely unconstrained)
|
||||
// we'll suggest the user constrain with a pessimistic constraint to
|
||||
// avoid implicitly adopting a later major release.
|
||||
constraintSuggestions := make(map[string]discovery.ConstraintStr)
|
||||
for name, meta := range chosen {
|
||||
req := requirements[name]
|
||||
if req == nil {
|
||||
// should never happen, but we don't want to crash here, so we'll
|
||||
// be cautious.
|
||||
continue
|
||||
}
|
||||
constraintSuggestions := make(map[string]string)
|
||||
for addr, version := range selected {
|
||||
req := reqs[addr]
|
||||
|
||||
if req.Versions.Unconstrained() && meta.Version != discovery.VersionZero {
|
||||
// meta.Version.MustParse is safe here because our "chosen" metas
|
||||
// were already filtered for validity of versions.
|
||||
constraintSuggestions[name] = meta.Version.MustParse().MinorUpgradeConstraintStr()
|
||||
if len(req) == 0 {
|
||||
constraintSuggestions[addr.ForDisplay()] = "~> " + version.String()
|
||||
}
|
||||
}
|
||||
if len(constraintSuggestions) != 0 {
|
||||
|
@ -624,7 +548,7 @@ func (c *InitCommand) getProviders(earlyConfig *earlyconfig.Config, state *state
|
|||
|
||||
c.Ui.Output(outputInitProvidersUnconstrained)
|
||||
for _, name := range names {
|
||||
c.Ui.Output(fmt.Sprintf("* provider.%s: version = %q", name, constraintSuggestions[name]))
|
||||
c.Ui.Output(fmt.Sprintf("* %s: version = %q", name, constraintSuggestions[name]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -856,9 +780,8 @@ The following providers do not have any version constraints in configuration,
|
|||
so the latest version was installed.
|
||||
|
||||
To prevent automatic upgrades to new major versions that may contain breaking
|
||||
changes, it is recommended to add version = "..." constraints to the
|
||||
corresponding provider blocks in configuration, with the constraint strings
|
||||
suggested below.
|
||||
changes, we recommend adding version constraints in a required_providers block
|
||||
in your configuration, with the constraint strings suggested below.
|
||||
`
|
||||
|
||||
const errDiscoveryServiceUnreachable = `
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,8 +2,6 @@ package command
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
)
|
||||
|
||||
func TestInternalPlugin_InternalProviders(t *testing.T) {
|
||||
|
@ -11,7 +9,7 @@ func TestInternalPlugin_InternalProviders(t *testing.T) {
|
|||
providers := m.internalProviders()
|
||||
// terraform is the only provider moved back to internal
|
||||
for _, name := range []string{"terraform"} {
|
||||
pf, ok := providers[addrs.NewLegacyProvider(name)]
|
||||
pf, ok := providers[name]
|
||||
if !ok {
|
||||
t.Errorf("Expected to find %s in InternalProviders", name)
|
||||
}
|
||||
|
|
|
@ -303,15 +303,13 @@ func marshalResources(resources map[string]*configs.Resource, schemas *terraform
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: get actual providerFqn
|
||||
providerFqn := addrs.NewLegacyProvider(v.ProviderConfigAddr().LocalName)
|
||||
schema, schemaVer := schemas.ResourceTypeConfig(
|
||||
providerFqn,
|
||||
v.Provider,
|
||||
v.Mode,
|
||||
v.Type,
|
||||
)
|
||||
if schema == nil {
|
||||
return nil, fmt.Errorf("no schema found for %s", v.Addr().String())
|
||||
return nil, fmt.Errorf("no schema found for %s (in provider %s)", v.Addr().String(), v.Provider)
|
||||
}
|
||||
r.SchemaVersion = schemaVer
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
|
|||
addr.Resource.Resource.Type,
|
||||
)
|
||||
if schema == nil {
|
||||
return fmt.Errorf("no schema found for %s", r.Address)
|
||||
return fmt.Errorf("no schema found for %s (in provider %s)", r.Address, rc.ProviderAddr.Provider)
|
||||
}
|
||||
|
||||
changeV, err := rc.Decode(schema.ImpliedType())
|
||||
|
@ -252,7 +252,7 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
|
|||
r.ModuleAddress = addr.Module.String()
|
||||
r.Name = addr.Resource.Resource.Name
|
||||
r.Type = addr.Resource.Resource.Type
|
||||
r.ProviderName = rc.ProviderAddr.Provider.LegacyString()
|
||||
r.ProviderName = rc.ProviderAddr.Provider.String()
|
||||
|
||||
p.ResourceChanges = append(p.ResourceChanges, r)
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ func marshalPlanResources(changes *plans.Changes, ris []addrs.AbsResourceInstanc
|
|||
Address: r.Addr.String(),
|
||||
Type: r.Addr.Resource.Resource.Type,
|
||||
Name: r.Addr.Resource.Resource.Name,
|
||||
ProviderName: r.ProviderAddr.Provider.LegacyString(),
|
||||
ProviderName: r.ProviderAddr.Provider.String(),
|
||||
Index: r.Addr.Resource.Key,
|
||||
}
|
||||
|
||||
|
|
|
@ -198,7 +198,7 @@ func TestMarshalPlanResources(t *testing.T) {
|
|||
Type: "test_thing",
|
||||
Name: "example",
|
||||
Index: addrs.InstanceKey(nil),
|
||||
ProviderName: "test",
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
SchemaVersion: 1,
|
||||
AttributeValues: attributeValues{},
|
||||
}},
|
||||
|
@ -227,7 +227,7 @@ func TestMarshalPlanResources(t *testing.T) {
|
|||
Type: "test_thing",
|
||||
Name: "example",
|
||||
Index: addrs.InstanceKey(nil),
|
||||
ProviderName: "test",
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
SchemaVersion: 1,
|
||||
AttributeValues: attributeValues{
|
||||
|
||||
|
@ -259,7 +259,7 @@ func TestMarshalPlanResources(t *testing.T) {
|
|||
Name: "example",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
ProviderAddr: addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
ChangeSrc: plans.ChangeSrc{
|
||||
|
@ -294,7 +294,7 @@ func TestMarshalPlanResources(t *testing.T) {
|
|||
func testSchemas() *terraform.Schemas {
|
||||
return &terraform.Schemas{
|
||||
Providers: map[addrs.Provider]*terraform.ProviderSchema{
|
||||
addrs.NewLegacyProvider("test"): &terraform.ProviderSchema{
|
||||
addrs.NewDefaultProvider("test"): &terraform.ProviderSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"test_thing": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
|
|
|
@ -35,7 +35,7 @@ func Marshal(s *terraform.Schemas) ([]byte, error) {
|
|||
providers := newProviders()
|
||||
|
||||
for k, v := range s.Providers {
|
||||
providers.Schemas[k.LegacyString()] = marshalProvider(v)
|
||||
providers.Schemas[k.String()] = marshalProvider(v)
|
||||
}
|
||||
|
||||
ret, err := json.Marshal(providers)
|
||||
|
|
|
@ -261,7 +261,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
|||
Address: r.Addr.Instance(k).String(),
|
||||
Type: resAddr.Type,
|
||||
Name: resAddr.Name,
|
||||
ProviderName: r.ProviderConfig.Provider.LegacyString(),
|
||||
ProviderName: r.ProviderConfig.Provider.String(),
|
||||
}
|
||||
|
||||
switch resAddr.Mode {
|
||||
|
@ -291,7 +291,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
|||
current.SchemaVersion = ri.Current.SchemaVersion
|
||||
|
||||
if schema == nil {
|
||||
return nil, fmt.Errorf("no schema found for %s", resAddr.String())
|
||||
return nil, fmt.Errorf("no schema found for %s (in provider %s)", resAddr.String(), r.ProviderConfig.Provider)
|
||||
}
|
||||
riObj, err := ri.Current.Decode(schema.ImpliedType())
|
||||
if err != nil {
|
||||
|
|
|
@ -204,7 +204,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
},
|
||||
},
|
||||
ProviderConfig: addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
},
|
||||
|
@ -217,7 +217,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
Type: "test_thing",
|
||||
Name: "bar",
|
||||
Index: addrs.InstanceKey(nil),
|
||||
ProviderName: "test",
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
SchemaVersion: 1,
|
||||
AttributeValues: attributeValues{
|
||||
"foozles": json.RawMessage(`null`),
|
||||
|
@ -248,7 +248,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
},
|
||||
},
|
||||
ProviderConfig: addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
},
|
||||
|
@ -261,7 +261,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
Type: "test_thing",
|
||||
Name: "bar",
|
||||
Index: addrs.IntKey(0),
|
||||
ProviderName: "test",
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
SchemaVersion: 1,
|
||||
AttributeValues: attributeValues{
|
||||
"foozles": json.RawMessage(`null`),
|
||||
|
@ -292,7 +292,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
},
|
||||
},
|
||||
ProviderConfig: addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
},
|
||||
|
@ -305,7 +305,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
Type: "test_thing",
|
||||
Name: "bar",
|
||||
Index: addrs.StringKey("rockhopper"),
|
||||
ProviderName: "test",
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
SchemaVersion: 1,
|
||||
AttributeValues: attributeValues{
|
||||
"foozles": json.RawMessage(`null`),
|
||||
|
@ -338,7 +338,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
},
|
||||
},
|
||||
ProviderConfig: addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
},
|
||||
|
@ -351,7 +351,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
Type: "test_thing",
|
||||
Name: "bar",
|
||||
Index: addrs.InstanceKey(nil),
|
||||
ProviderName: "test",
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
DeposedKey: deposedKey.String(),
|
||||
AttributeValues: attributeValues{
|
||||
"foozles": json.RawMessage(`null`),
|
||||
|
@ -389,7 +389,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
},
|
||||
},
|
||||
ProviderConfig: addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
},
|
||||
|
@ -402,7 +402,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
Type: "test_thing",
|
||||
Name: "bar",
|
||||
Index: addrs.InstanceKey(nil),
|
||||
ProviderName: "test",
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
SchemaVersion: 1,
|
||||
AttributeValues: attributeValues{
|
||||
"foozles": json.RawMessage(`null`),
|
||||
|
@ -415,7 +415,7 @@ func TestMarshalResources(t *testing.T) {
|
|||
Type: "test_thing",
|
||||
Name: "bar",
|
||||
Index: addrs.InstanceKey(nil),
|
||||
ProviderName: "test",
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
DeposedKey: deposedKey.String(),
|
||||
AttributeValues: attributeValues{
|
||||
"foozles": json.RawMessage(`null`),
|
||||
|
@ -461,7 +461,7 @@ func TestMarshalModules_basic(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -476,7 +476,7 @@ func TestMarshalModules_basic(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childModule.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -491,7 +491,7 @@ func TestMarshalModules_basic(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: subModule.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -530,7 +530,7 @@ func TestMarshalModules_nested(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -545,7 +545,7 @@ func TestMarshalModules_nested(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childModule.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -560,7 +560,7 @@ func TestMarshalModules_nested(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: subModule.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -591,7 +591,7 @@ func TestMarshalModules_nested(t *testing.T) {
|
|||
func testSchemas() *terraform.Schemas {
|
||||
return &terraform.Schemas{
|
||||
Providers: map[addrs.Provider]*terraform.ProviderSchema{
|
||||
addrs.NewLegacyProvider("test"): &terraform.ProviderSchema{
|
||||
addrs.NewDefaultProvider("test"): &terraform.ProviderSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"test_thing": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
|
|
|
@ -187,8 +187,8 @@ type PluginOverrides struct {
|
|||
}
|
||||
|
||||
type testingOverrides struct {
|
||||
ProviderResolver providers.Resolver
|
||||
Provisioners map[string]provisioners.Factory
|
||||
Providers map[addrs.Provider]providers.Factory
|
||||
Provisioners map[string]provisioners.Factory
|
||||
}
|
||||
|
||||
// initStatePaths is used to initialize the default values for
|
||||
|
@ -350,10 +350,22 @@ func (m *Meta) contextOpts() *terraform.ContextOpts {
|
|||
// and just work with what we've been given, thus allowing the tests
|
||||
// to provide mock providers and provisioners.
|
||||
if m.testingOverrides != nil {
|
||||
opts.ProviderResolver = m.testingOverrides.ProviderResolver
|
||||
opts.Providers = m.testingOverrides.Providers
|
||||
opts.Provisioners = m.testingOverrides.Provisioners
|
||||
} 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()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,90 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"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/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)
|
||||
}
|
||||
var builtinProviderTypes []string
|
||||
for ty := range m.internalProviders() {
|
||||
builtinProviderTypes = append(builtinProviderTypes, ty)
|
||||
}
|
||||
inst.SetBuiltInProviderTypes(builtinProviderTypes)
|
||||
return inst
|
||||
}
|
||||
|
||||
// providerCustomLocalDirectorySource produces a provider source that consults
|
||||
// only the given local filesystem directories for plugins to install.
|
||||
//
|
||||
// This is used to implement the -plugin-dir option for "terraform init", where
|
||||
// the result of this method is used instead of what would've been returned
|
||||
// from m.providerInstallSource.
|
||||
//
|
||||
// If the given list of directories is empty then the resulting source will
|
||||
// have no providers available for installation at all.
|
||||
func (m *Meta) providerCustomLocalDirectorySource(dirs []string) getproviders.Source {
|
||||
var ret getproviders.MultiSource
|
||||
for _, dir := range dirs {
|
||||
ret = append(ret, getproviders.MultiSourceSelector{
|
||||
Source: getproviders.NewFilesystemMirrorSource(dir),
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// providerLocalCacheDir returns an object representing the
|
||||
// configuration-specific local cache directory. This is the
|
||||
// only location consulted for provider plugin packages for Terraform
|
||||
|
@ -15,6 +93,9 @@ import (
|
|||
// Only the provider installer (in "terraform init") is permitted to make
|
||||
// modifications to this cache directory. All other commands must treat it
|
||||
// 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 {
|
||||
dir := filepath.Join(m.DataDir(), "plugins")
|
||||
if dir == "" {
|
||||
|
@ -30,6 +111,9 @@ func (m *Meta) providerLocalCacheDir() *providercache.Dir {
|
|||
// This function may return nil, in which case there is no global cache
|
||||
// configured and new packages should be downloaded directly into individual
|
||||
// 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 {
|
||||
dir := m.PluginCacheDir
|
||||
if dir == "" {
|
||||
|
@ -61,3 +145,85 @@ func (m *Meta) providerInstallSource() getproviders.Source {
|
|||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ func TestPlan_destroy(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -244,7 +244,7 @@ func TestPlan_outPathNoChange(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -286,26 +286,23 @@ func TestPlan_outBackend(t *testing.T) {
|
|||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
// Our state
|
||||
originalState := &terraform.State{
|
||||
Modules: []*terraform.ModuleState{
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root"},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_instance.foo": &terraform.ResourceState{
|
||||
Type: "test_instance",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "bar",
|
||||
Attributes: map[string]string{
|
||||
"ami": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
originalState := states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_instance",
|
||||
Name: "foo",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
AttrsJSON: []byte(`{"id":"bar","ami":"bar"}`),
|
||||
Status: states.ObjectReady,
|
||||
},
|
||||
},
|
||||
}
|
||||
originalState.Init()
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
// Setup our backend state
|
||||
dataState, srv := testBackendState(t, originalState, 200)
|
||||
|
|
|
@ -2,7 +2,6 @@ package command
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
@ -15,104 +14,19 @@ import (
|
|||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/kardianos/osext"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
terraformProvider "github.com/hashicorp/terraform/builtin/providers/terraform"
|
||||
tfplugin "github.com/hashicorp/terraform/plugin"
|
||||
"github.com/hashicorp/terraform/plugin/discovery"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/provisioners"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// multiVersionProviderResolver is an implementation of
|
||||
// terraform.ResourceProviderResolver that matches the given version constraints
|
||||
// against a set of versioned provider plugins to find the newest version of
|
||||
// each that satisfies the given constraints.
|
||||
type multiVersionProviderResolver struct {
|
||||
Available discovery.PluginMetaSet
|
||||
|
||||
// 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
|
||||
}
|
||||
// NOTE WELL: The logic in this file is primarily about plugin types OTHER THAN
|
||||
// providers, which use an older set of approaches implemented here.
|
||||
//
|
||||
// The provider-related functions live primarily in meta_providers.go, and
|
||||
// lean on some different underlying mechanisms in order to support automatic
|
||||
// installation and a heirarchical addressing namespace, neither of which
|
||||
// are supported for other plugin types.
|
||||
|
||||
// store the user-supplied path for plugin discovery
|
||||
func (m *Meta) storePluginPath(pluginPath []string) error {
|
||||
|
@ -216,101 +130,6 @@ func (m *Meta) pluginCache() discovery.PluginCache {
|
|||
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 {
|
||||
dirs := m.pluginDirs(true)
|
||||
plugins := discovery.FindPlugins("provisioner", dirs)
|
||||
|
@ -364,28 +183,6 @@ func internalPluginClient(kind, name string) (*plugin.Client, error) {
|
|||
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 {
|
||||
return func() (provisioners.Interface, error) {
|
||||
client := tfplugin.Client(meta)
|
||||
|
|
|
@ -1,102 +1,11 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/plugin/discovery"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
func TestMultiVersionProviderResolver(t *testing.T) {
|
||||
available := make(discovery.PluginMetaSet)
|
||||
available.Add(discovery.PluginMeta{
|
||||
Name: "plugin",
|
||||
Version: "1.0.0",
|
||||
Path: "testdata/empty-file",
|
||||
})
|
||||
|
||||
resolver := &multiVersionProviderResolver{
|
||||
Internal: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("internal"): providers.FactoryFixed(
|
||||
&terraform.MockProvider{
|
||||
GetSchemaReturn: &terraform.ProviderSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"internal_foo": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
Available: available,
|
||||
}
|
||||
|
||||
t.Run("plugin matches", func(t *testing.T) {
|
||||
reqd := discovery.PluginRequirements{
|
||||
"plugin": &discovery.PluginConstraints{
|
||||
Versions: discovery.ConstraintStr("1.0.0").MustParse(),
|
||||
},
|
||||
}
|
||||
got, err := resolver.ResolveProviders(reqd)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if ct := len(got); ct != 1 {
|
||||
t.Errorf("wrong number of results %d; want 1", ct)
|
||||
}
|
||||
if _, exists := got[addrs.NewLegacyProvider("plugin")]; !exists {
|
||||
t.Errorf("provider \"plugin\" not in result")
|
||||
}
|
||||
})
|
||||
t.Run("plugin doesn't match", func(t *testing.T) {
|
||||
reqd := discovery.PluginRequirements{
|
||||
"plugin": &discovery.PluginConstraints{
|
||||
Versions: discovery.ConstraintStr("2.0.0").MustParse(),
|
||||
},
|
||||
}
|
||||
_, err := resolver.ResolveProviders(reqd)
|
||||
if err == nil {
|
||||
t.Errorf("resolved successfully, but want error")
|
||||
}
|
||||
})
|
||||
t.Run("internal matches", func(t *testing.T) {
|
||||
reqd := discovery.PluginRequirements{
|
||||
"internal": &discovery.PluginConstraints{
|
||||
Versions: discovery.AllVersions,
|
||||
},
|
||||
}
|
||||
got, err := resolver.ResolveProviders(reqd)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if ct := len(got); ct != 1 {
|
||||
t.Errorf("wrong number of results %d; want 1", ct)
|
||||
}
|
||||
if _, exists := got[addrs.NewLegacyProvider("internal")]; !exists {
|
||||
t.Errorf("provider \"internal\" not in result")
|
||||
}
|
||||
})
|
||||
t.Run("internal with version constraint", func(t *testing.T) {
|
||||
// Version constraints are not permitted for internal providers
|
||||
reqd := discovery.PluginRequirements{
|
||||
"internal": &discovery.PluginConstraints{
|
||||
Versions: discovery.ConstraintStr("2.0.0").MustParse(),
|
||||
},
|
||||
}
|
||||
_, err := resolver.ResolveProviders(reqd)
|
||||
if err == nil {
|
||||
t.Errorf("resolved successfully, but want error")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginPath(t *testing.T) {
|
||||
td := testTempDir(t)
|
||||
defer os.RemoveAll(td)
|
||||
|
@ -122,7 +31,7 @@ func TestPluginPath(t *testing.T) {
|
|||
func TestInternalProviders(t *testing.T) {
|
||||
m := Meta{}
|
||||
internal := m.internalProviders()
|
||||
tfProvider, err := internal[addrs.NewLegacyProvider("terraform")]()
|
||||
tfProvider, err := internal["terraform"]()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -133,79 +42,3 @@ func TestInternalProviders(t *testing.T) {
|
|||
t.Errorf("didn't find terraform_remote_state in internal \"terraform\" provider")
|
||||
}
|
||||
}
|
||||
|
||||
// mockProviderInstaller is a discovery.PluginInstaller implementation that
|
||||
// is a mock for discovery.ProviderInstaller.
|
||||
type mockProviderInstaller struct {
|
||||
// A map of provider names to available versions.
|
||||
// The tests expect the versions to be in order from newest to oldest.
|
||||
Providers map[string][]string
|
||||
|
||||
Dir string
|
||||
PurgeUnusedCalled bool
|
||||
}
|
||||
|
||||
func (i *mockProviderInstaller) FileName(provider, version string) string {
|
||||
return fmt.Sprintf("terraform-provider-%s_v%s_x4", provider, version)
|
||||
}
|
||||
|
||||
func (i *mockProviderInstaller) Get(provider addrs.Provider, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
|
||||
var diags tfdiags.Diagnostics
|
||||
noMeta := discovery.PluginMeta{}
|
||||
versions := i.Providers[provider.Type]
|
||||
if len(versions) == 0 {
|
||||
return noMeta, diags, fmt.Errorf("provider %q not found", provider)
|
||||
}
|
||||
|
||||
err := os.MkdirAll(i.Dir, 0755)
|
||||
if err != nil {
|
||||
return noMeta, diags, fmt.Errorf("error creating plugins directory: %s", err)
|
||||
}
|
||||
|
||||
for _, v := range versions {
|
||||
version, err := discovery.VersionStr(v).Parse()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if req.Allows(version) {
|
||||
// provider filename
|
||||
name := i.FileName(provider.Type, v)
|
||||
path := filepath.Join(i.Dir, name)
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return noMeta, diags, fmt.Errorf("error fetching provider: %s", err)
|
||||
}
|
||||
f.Close()
|
||||
return discovery.PluginMeta{
|
||||
Name: provider.Type,
|
||||
Version: discovery.VersionStr(v),
|
||||
Path: path,
|
||||
}, diags, nil
|
||||
}
|
||||
}
|
||||
|
||||
return noMeta, diags, fmt.Errorf("no suitable version for provider %q found with constraints %s", provider, req)
|
||||
}
|
||||
|
||||
func (i *mockProviderInstaller) PurgeUnused(map[string]discovery.PluginMeta) (discovery.PluginMetaSet, error) {
|
||||
i.PurgeUnusedCalled = true
|
||||
ret := make(discovery.PluginMetaSet)
|
||||
ret.Add(discovery.PluginMeta{
|
||||
Name: "test",
|
||||
Version: "0.0.0",
|
||||
Path: "mock-test",
|
||||
})
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type callbackPluginInstaller func(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error)
|
||||
|
||||
func (cb callbackPluginInstaller) Get(provider addrs.Provider, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
|
||||
return cb(provider.Type, req)
|
||||
}
|
||||
|
||||
func (cb callbackPluginInstaller) PurgeUnused(map[string]discovery.PluginMeta) (discovery.PluginMetaSet, error) {
|
||||
// does nothing
|
||||
return make(discovery.PluginMetaSet), nil
|
||||
}
|
||||
|
|
|
@ -2,13 +2,9 @@ package command
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/moduledeps"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/xlab/treeprint"
|
||||
)
|
||||
|
||||
|
@ -35,81 +31,84 @@ func (c *ProvidersCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
configPath, err := ModulePath(cmdFlags.Args())
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
empty, err := configs.IsEmptyDir(configPath)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Error validating configuration directory",
|
||||
fmt.Sprintf("Terraform encountered an unexpected error while verifying that the given configuration directory is valid: %s.", err),
|
||||
))
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
if empty {
|
||||
absPath, err := filepath.Abs(configPath)
|
||||
/*
|
||||
configPath, err := ModulePath(cmdFlags.Args())
|
||||
if err != nil {
|
||||
absPath = configPath
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"No configuration files",
|
||||
fmt.Sprintf("The directory %s contains no Terraform configuration files.", absPath),
|
||||
))
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
empty, err := configs.IsEmptyDir(configPath)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Error validating configuration directory",
|
||||
fmt.Sprintf("Terraform encountered an unexpected error while verifying that the given configuration directory is valid: %s.", err),
|
||||
))
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
if empty {
|
||||
absPath, err := filepath.Abs(configPath)
|
||||
if err != nil {
|
||||
absPath = configPath
|
||||
}
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"No configuration files",
|
||||
fmt.Sprintf("The directory %s contains no Terraform configuration files.", absPath),
|
||||
))
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
config, configDiags := c.loadConfig(configPath)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: config.Module.Backend,
|
||||
})
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Get the state
|
||||
env := c.Workspace()
|
||||
state, err := b.StateMgr(env)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
||||
return 1
|
||||
}
|
||||
if err := state.RefreshState(); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
s := state.State()
|
||||
depTree := terraform.ConfigTreeDependencies(config, s)
|
||||
depTree.SortDescendents()
|
||||
|
||||
printRoot := treeprint.New()
|
||||
providersCommandPopulateTreeNode(printRoot, depTree)
|
||||
|
||||
c.Ui.Output(printRoot.String())
|
||||
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
config, configDiags := c.loadConfig(configPath)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: config.Module.Backend,
|
||||
})
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Get the state
|
||||
env := c.Workspace()
|
||||
state, err := b.StateMgr(env)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
||||
return 1
|
||||
}
|
||||
if err := state.RefreshState(); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
s := state.State()
|
||||
depTree := terraform.ConfigTreeDependencies(config, s)
|
||||
depTree.SortDescendents()
|
||||
|
||||
printRoot := treeprint.New()
|
||||
providersCommandPopulateTreeNode(printRoot, depTree)
|
||||
|
||||
c.Ui.Output(printRoot.String())
|
||||
|
||||
c.showDiagnostics(diags)
|
||||
if diags.HasErrors() {
|
||||
return 1
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
return 1
|
||||
}
|
||||
*/
|
||||
|
||||
c.Ui.Output(fmt.Sprintf("terraform providers is temporarily disabled"))
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
@ -49,22 +49,22 @@ func TestProvidersSchema_output(t *testing.T) {
|
|||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
providerSource, close := newMockProviderSource(t, map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
})
|
||||
defer close()
|
||||
|
||||
p := showFixtureProvider()
|
||||
ui := new(cli.MockUi)
|
||||
m := Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
ProviderSource: providerSource,
|
||||
}
|
||||
|
||||
// `terrafrom init`
|
||||
ic := &InitCommand{
|
||||
Meta: m,
|
||||
providerInstaller: &mockProviderInstaller{
|
||||
Providers: map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
},
|
||||
Dir: m.pluginDir(),
|
||||
},
|
||||
}
|
||||
if code := ic.Run([]string{}); code != 0 {
|
||||
t.Fatalf("init failed\n%s", ui.ErrorWriter)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestProviders(t *testing.T) {
|
||||
return
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
|
@ -43,6 +44,7 @@ func TestProviders(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestProviders_noConfigs(t *testing.T) {
|
||||
return
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
|
|
|
@ -757,10 +757,10 @@ foo = "bar"
|
|||
const testRefreshStr = `
|
||||
test_instance.foo:
|
||||
ID = yes
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
`
|
||||
const testRefreshCwdStr = `
|
||||
test_instance.foo:
|
||||
ID = yes
|
||||
provider = provider["registry.terraform.io/-/test"]
|
||||
provider = provider["registry.terraform.io/hashicorp/test"]
|
||||
`
|
||||
|
|
|
@ -85,7 +85,7 @@ func TestShow_aliasedProvider(t *testing.T) {
|
|||
Dependencies: []addrs.ConfigResource{},
|
||||
DependsOn: []addrs.Referenceable{},
|
||||
},
|
||||
addrs.RootModuleInstance.ProviderConfigAliased(addrs.NewLegacyProvider("test"), "alias"),
|
||||
addrs.RootModuleInstance.ProviderConfigAliased(addrs.NewDefaultProvider("test"), "alias"),
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -250,22 +250,22 @@ func TestShow_json_output(t *testing.T) {
|
|||
|
||||
expectError := strings.Contains(entry.Name(), "error")
|
||||
|
||||
providerSource, close := newMockProviderSource(t, map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
})
|
||||
defer close()
|
||||
|
||||
p := showFixtureProvider()
|
||||
ui := new(cli.MockUi)
|
||||
m := Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
ProviderSource: providerSource,
|
||||
}
|
||||
|
||||
// init
|
||||
ic := &InitCommand{
|
||||
Meta: m,
|
||||
providerInstaller: &mockProviderInstaller{
|
||||
Providers: map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
},
|
||||
Dir: m.pluginDir(),
|
||||
},
|
||||
}
|
||||
if code := ic.Run([]string{}); code != 0 {
|
||||
if expectError {
|
||||
|
@ -347,22 +347,22 @@ func TestShow_json_output_state(t *testing.T) {
|
|||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
providerSource, close := newMockProviderSource(t, map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
})
|
||||
defer close()
|
||||
|
||||
p := showFixtureProvider()
|
||||
ui := new(cli.MockUi)
|
||||
m := Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
ProviderSource: providerSource,
|
||||
}
|
||||
|
||||
// init
|
||||
ic := &InitCommand{
|
||||
Meta: m,
|
||||
providerInstaller: &mockProviderInstaller{
|
||||
Providers: map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
},
|
||||
Dir: m.pluginDir(),
|
||||
},
|
||||
}
|
||||
if code := ic.Run([]string{}); code != 0 {
|
||||
t.Fatalf("init failed\n%s", ui.ErrorWriter)
|
||||
|
@ -486,7 +486,7 @@ func showFixturePlanFile(t *testing.T, action plans.Action) string {
|
|||
Name: "foo",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
ProviderAddr: addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
ChangeSrc: plans.ChangeSrc{
|
||||
|
|
|
@ -26,7 +26,7 @@ func TestStateShow(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -84,7 +84,7 @@ func TestStateShow_multi(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -99,7 +99,7 @@ func TestStateShow_multi(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: submod.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -205,7 +205,7 @@ func TestStateShow_configured_provider(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test-beta"),
|
||||
Provider: addrs.NewDefaultProvider("test-beta"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -229,11 +229,9 @@ func TestStateShow_configured_provider(t *testing.T) {
|
|||
c := &StateShowCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: &testingOverrides{
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("test-beta"): providers.FactoryFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test-beta"): providers.FactoryFixed(p),
|
||||
},
|
||||
},
|
||||
Ui: ui,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
nonexist = {
|
||||
source = "terraform.io/builtin/nonexist"
|
||||
}
|
||||
terraform = {
|
||||
version = "1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"format_version": "0.1",
|
||||
"provider_schemas": {
|
||||
"test": {
|
||||
"registry.terraform.io/hashicorp/test": {
|
||||
"resource_schemas": {
|
||||
"test_instance": {
|
||||
"version": 0,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "example",
|
||||
"index": 0,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": null,
|
||||
|
@ -23,7 +23,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "example",
|
||||
"index": 1,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": null,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "example",
|
||||
"each": "list",
|
||||
"provider": "provider.test",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"index_key": 0,
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "example",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar-var",
|
||||
|
@ -35,7 +35,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "example",
|
||||
"index": 0,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "foo-var",
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "example",
|
||||
"each": "list",
|
||||
"provider": "provider.test",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"index_key": 0,
|
||||
|
@ -33,7 +33,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "example",
|
||||
"provider": "provider.test",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
|
@ -32,7 +32,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
|
@ -44,7 +44,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
|
@ -71,7 +71,7 @@
|
|||
"index": 0,
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test",
|
||||
"change": {
|
||||
"actions": [
|
||||
|
@ -91,7 +91,7 @@
|
|||
"index": 1,
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test",
|
||||
"change": {
|
||||
"actions": [
|
||||
|
@ -111,7 +111,7 @@
|
|||
"index": 2,
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test",
|
||||
"change": {
|
||||
"actions": [
|
||||
|
@ -175,4 +175,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
|
@ -34,7 +34,7 @@
|
|||
"address": "test_instance.test",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test",
|
||||
"change": {
|
||||
"actions": [
|
||||
|
@ -55,7 +55,7 @@
|
|||
"address": "test_instance.test-delete",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test-delete",
|
||||
"change": {
|
||||
"actions": [
|
||||
|
@ -97,7 +97,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"values": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
|
@ -109,7 +109,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test-delete",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"values": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
|
@ -154,4 +154,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider": "provider[\"registry.terraform.io/-/test\"]",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
|
@ -24,7 +24,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test-delete",
|
||||
"provider": "provider[\"registry.terraform.io/-/test\"]",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
|
@ -34,7 +34,7 @@
|
|||
"address": "test_instance.test",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test",
|
||||
"change": {
|
||||
"actions": [
|
||||
|
@ -79,7 +79,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"schema_version": 0,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
|
@ -124,4 +124,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider": "provider[\"registry.terraform.io/-/test\"]",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar-var"
|
||||
|
@ -33,7 +33,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"index": 0,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "baz"
|
||||
|
@ -45,7 +45,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"index": 1,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "baz"
|
||||
|
@ -57,7 +57,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"index": 2,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "baz"
|
||||
|
@ -88,7 +88,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"change": {
|
||||
"actions": [
|
||||
"create"
|
||||
|
@ -107,7 +107,7 @@
|
|||
"module_address": "module.module_test_foo",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test",
|
||||
"index": 0,
|
||||
"change": {
|
||||
|
@ -128,7 +128,7 @@
|
|||
"module_address": "module.module_test_foo",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test",
|
||||
"index": 1,
|
||||
"change": {
|
||||
|
@ -149,7 +149,7 @@
|
|||
"module_address": "module.module_test_foo",
|
||||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"name": "test",
|
||||
"index": 2,
|
||||
"change": {
|
||||
|
@ -280,4 +280,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"index": 0,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
|
@ -34,7 +34,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"index": 1,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
|
@ -50,7 +50,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"index": 0,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"change": {
|
||||
"actions": [
|
||||
"no-op"
|
||||
|
@ -72,7 +72,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"index": 1,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"change": {
|
||||
"actions": [
|
||||
"create"
|
||||
|
@ -115,7 +115,7 @@
|
|||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"index": 0,
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider": "provider[\"registry.terraform.io/-/test\"]",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar-var"
|
||||
|
@ -34,7 +34,7 @@
|
|||
"mode": "managed",
|
||||
"type": "test_instance",
|
||||
"name": "test",
|
||||
"provider_name": "test",
|
||||
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||
"change": {
|
||||
"actions": [
|
||||
"create"
|
||||
|
@ -89,4 +89,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// result in the usual case where the user successfully ran "terraform init"
|
||||
// and then hit a problem running _another_ command.
|
||||
providerPlugins := c.providerPluginSet()
|
||||
pluginsLockFile := c.providerPluginsLock()
|
||||
pluginsLock := pluginsLockFile.Read()
|
||||
providerInstaller := c.providerInstaller()
|
||||
providerSelections, err := providerInstaller.SelectedPackages()
|
||||
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 we can't read the file to hash it, ignore it.
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(gotHash, wantHash) {
|
||||
// Not the plugin we've locked, so ignore it.
|
||||
continue
|
||||
}
|
||||
|
||||
// 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))
|
||||
if err != nil {
|
||||
// we'll just ignore it and show no plugins at all, then.
|
||||
providerSelections = nil
|
||||
}
|
||||
for providerAddr, cached := range providerSelections {
|
||||
version := cached.Version.String()
|
||||
if version == "0.0.0" {
|
||||
pluginVersions = append(pluginVersions, fmt.Sprintf("+ provider %s (unversioned)", providerAddr))
|
||||
} 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 {
|
||||
|
|
|
@ -310,10 +310,7 @@ func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addr
|
|||
if providerReq, exists := c.Module.ProviderRequirements[addr.LocalName]; exists {
|
||||
provider = providerReq.Type
|
||||
} else {
|
||||
// FIXME: For now we're returning a _legacy_ address as fallback here,
|
||||
// but once we remove legacy addresses this should actually be a
|
||||
// _default_ provider address.
|
||||
provider = addrs.NewLegacyProvider(addr.LocalName)
|
||||
provider = addrs.ImpliedProviderForUnqualifiedType(addr.LocalName)
|
||||
}
|
||||
|
||||
return addrs.AbsProviderConfig{
|
||||
|
@ -330,7 +327,7 @@ func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addr
|
|||
|
||||
// ProviderForConfigAddr returns the FQN for a given addrs.ProviderConfig, first
|
||||
// by checking for the provider in module.ProviderRequirements and falling
|
||||
// back to addrs.NewLegacyProvider if it is not found.
|
||||
// back to addrs.NewDefaultProvider 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
|
||||
|
|
|
@ -25,9 +25,9 @@ func TestConfigProviderTypes(t *testing.T) {
|
|||
|
||||
got = cfg.ProviderTypes()
|
||||
want := []addrs.Provider{
|
||||
addrs.NewLegacyProvider("aws"),
|
||||
addrs.NewLegacyProvider("null"),
|
||||
addrs.NewLegacyProvider("template"),
|
||||
addrs.NewDefaultProvider("aws"),
|
||||
addrs.NewDefaultProvider("null"),
|
||||
addrs.NewDefaultProvider("template"),
|
||||
}
|
||||
for _, problem := range deep.Equal(got, want) {
|
||||
t.Error(problem)
|
||||
|
@ -50,10 +50,9 @@ func TestConfigProviderTypes_nested(t *testing.T) {
|
|||
|
||||
got = cfg.ProviderTypes()
|
||||
want := []addrs.Provider{
|
||||
// FIXME: this will be updated to NewDefaultProvider as we remove `Legacy*`
|
||||
addrs.NewLegacyProvider("test"),
|
||||
addrs.NewProvider(addrs.DefaultRegistryHost, "bar", "test"),
|
||||
addrs.NewProvider(addrs.DefaultRegistryHost, "foo", "test"),
|
||||
addrs.NewDefaultProvider("test"),
|
||||
}
|
||||
|
||||
for _, problem := range deep.Equal(got, want) {
|
||||
|
@ -85,14 +84,8 @@ func TestConfigResolveAbsProviderAddr(t *testing.T) {
|
|||
}
|
||||
got := cfg.ResolveAbsProviderAddr(addr, addrs.RootModule)
|
||||
want := addrs.AbsProviderConfig{
|
||||
Module: addrs.RootModule,
|
||||
// FIXME: At the time of writing we still have LocalProviderConfig
|
||||
// nested inside AbsProviderConfig, but a future change will
|
||||
// stop tis embedding and just have an addrs.Provider and an alias
|
||||
// string here, at which point the correct result will be:
|
||||
// Provider as the addrs repr of "registry.terraform.io/hashicorp/implied"
|
||||
// Alias as "boop".
|
||||
Provider: addrs.NewLegacyProvider("implied"),
|
||||
Module: addrs.RootModule,
|
||||
Provider: addrs.NewDefaultProvider("implied"),
|
||||
Alias: "boop",
|
||||
}
|
||||
if got, want := got.String(), want.String(); got != want {
|
||||
|
@ -128,13 +121,10 @@ func TestConfigProviderRequirements(t *testing.T) {
|
|||
svchost.Hostname("tf.example.com"),
|
||||
"awesomecorp", "happycloud",
|
||||
)
|
||||
// FIXME: these two are legacy ones for now because the config loader
|
||||
// isn't using default configurations fully yet.
|
||||
// Once that changes, these should be default-shaped ones like tlsProvider
|
||||
// above.
|
||||
nullProvider := addrs.NewLegacyProvider("null")
|
||||
randomProvider := addrs.NewLegacyProvider("random")
|
||||
impliedProvider := addrs.NewLegacyProvider("implied")
|
||||
nullProvider := addrs.NewDefaultProvider("null")
|
||||
randomProvider := addrs.NewDefaultProvider("random")
|
||||
impliedProvider := addrs.NewDefaultProvider("implied")
|
||||
terraformProvider := addrs.NewBuiltInProvider("terraform")
|
||||
|
||||
got, diags := cfg.ProviderRequirements()
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
@ -145,6 +135,7 @@ func TestConfigProviderRequirements(t *testing.T) {
|
|||
tlsProvider: getproviders.MustParseVersionConstraints("~> 3.0"),
|
||||
impliedProvider: nil,
|
||||
happycloudProvider: nil,
|
||||
terraformProvider: nil,
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
|
@ -152,7 +143,7 @@ func TestConfigProviderRequirements(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProviderForConfigAddr(t *testing.T) {
|
||||
func TestConfigProviderForConfigAddr(t *testing.T) {
|
||||
cfg, diags := testModuleConfigFromDir("testdata/valid-modules/providers-fqns")
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
|
@ -162,9 +153,9 @@ func TestProviderForConfigAddr(t *testing.T) {
|
|||
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
// now check a provider that isn't in the configuration. It should return a NewLegacyProvider.
|
||||
// now check a provider that isn't in the configuration. It should return a DefaultProvider.
|
||||
got = cfg.ProviderForConfigAddr(addrs.NewDefaultLocalProviderConfig("bar-test"))
|
||||
want = addrs.NewLegacyProvider("bar-test")
|
||||
want = addrs.NewDefaultProvider("bar-test")
|
||||
if !got.Equals(want) {
|
||||
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
|
|
@ -195,7 +195,7 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
|
|||
}
|
||||
diags = append(diags, hclDiags...)
|
||||
} else {
|
||||
fqn = addrs.NewLegacyProvider(reqd.Name)
|
||||
fqn = addrs.ImpliedProviderForUnqualifiedType(reqd.Name)
|
||||
}
|
||||
if existing, exists := m.ProviderRequirements[reqd.Name]; exists {
|
||||
if existing.Type != fqn {
|
||||
|
@ -208,8 +208,8 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
|
|||
}
|
||||
|
||||
for _, pm := range file.ProviderMetas {
|
||||
// TODO(paddy): pm.Provider is a string, but we need to build an addrs.Provider out of it somehow
|
||||
if existing, exists := m.ProviderMetas[addrs.NewLegacyProvider(pm.Provider)]; exists {
|
||||
provider := m.ProviderForLocalConfig(addrs.LocalProviderConfig{LocalName: pm.Provider})
|
||||
if existing, exists := m.ProviderMetas[provider]; exists {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Duplicate provider_meta block",
|
||||
|
@ -217,7 +217,7 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
|
|||
Subject: &pm.DeclRange,
|
||||
})
|
||||
}
|
||||
m.ProviderMetas[addrs.NewLegacyProvider(pm.Provider)] = pm
|
||||
m.ProviderMetas[provider] = pm
|
||||
}
|
||||
|
||||
for _, v := range file.Variables {
|
||||
|
@ -282,20 +282,15 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
|
|||
m.ManagedResources[key] = r
|
||||
|
||||
// set the provider FQN for the resource
|
||||
var provider addrs.Provider
|
||||
if r.ProviderConfigRef != nil {
|
||||
if existing, exists := m.ProviderRequirements[r.ProviderConfigAddr().LocalName]; exists {
|
||||
provider = existing.Type
|
||||
r.Provider = existing.Type
|
||||
} else {
|
||||
// FIXME: This will be a NewDefaultProvider
|
||||
provider = addrs.NewLegacyProvider(r.ProviderConfigAddr().LocalName)
|
||||
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigAddr().LocalName)
|
||||
}
|
||||
r.Provider = provider
|
||||
continue
|
||||
}
|
||||
// FIXME: this will replaced with NewDefaultProvider when provider
|
||||
// source is fully implemented.
|
||||
r.Provider = addrs.NewLegacyProvider(r.Addr().ImpliedProvider())
|
||||
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.Addr().ImpliedProvider())
|
||||
}
|
||||
|
||||
for _, r := range file.DataResources {
|
||||
|
@ -312,20 +307,15 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
|
|||
m.DataResources[key] = r
|
||||
|
||||
// set the provider FQN for the resource
|
||||
var provider addrs.Provider
|
||||
if r.ProviderConfigRef != nil {
|
||||
if existing, exists := m.ProviderRequirements[r.ProviderConfigAddr().LocalName]; exists {
|
||||
provider = existing.Type
|
||||
r.Provider = existing.Type
|
||||
} else {
|
||||
// FIXME: This will be a NewDefaultProvider
|
||||
provider = addrs.NewLegacyProvider(r.ProviderConfigAddr().LocalName)
|
||||
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigAddr().LocalName)
|
||||
}
|
||||
r.Provider = provider
|
||||
continue
|
||||
}
|
||||
// FIXME: this will replaced with NewDefaultProvider when provider
|
||||
// source is fully implemented.
|
||||
r.Provider = addrs.NewLegacyProvider(r.Addr().ImpliedProvider())
|
||||
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.Addr().ImpliedProvider())
|
||||
}
|
||||
|
||||
return diags
|
||||
|
@ -520,5 +510,5 @@ func (m *Module) ProviderForLocalConfig(pc addrs.LocalProviderConfig) addrs.Prov
|
|||
if provider, exists := m.ProviderRequirements[pc.LocalName]; exists {
|
||||
return provider.Type
|
||||
}
|
||||
return addrs.NewLegacyProvider(pc.LocalName)
|
||||
return addrs.ImpliedProviderForUnqualifiedType(pc.LocalName)
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ func mergeProviderVersionConstraints(recv map[string]ProviderRequirements, ovrd
|
|||
// any errors parsing the source string will have already been captured.
|
||||
fqn, _ = addrs.ParseProviderSourceString(reqd.Source.SourceStr)
|
||||
} else {
|
||||
fqn = addrs.NewLegacyProvider(reqd.Name)
|
||||
fqn = addrs.ImpliedProviderForUnqualifiedType(reqd.Name)
|
||||
}
|
||||
recv[reqd.Name] = ProviderRequirements{Type: fqn, VersionConstraints: []VersionConstraint{reqd.Requirement}}
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ func (r *Resource) merge(or *Resource, prs map[string]ProviderRequirements) hcl.
|
|||
if existing, exists := prs[or.ProviderConfigRef.Name]; exists {
|
||||
r.Provider = existing.Type
|
||||
} else {
|
||||
r.Provider = addrs.NewLegacyProvider(r.ProviderConfigRef.Name)
|
||||
r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigRef.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ func TestModuleOverrideResourceFQNs(t *testing.T) {
|
|||
|
||||
// now verify that a resource with no provider config falls back to default
|
||||
got = mod.ManagedResources["test_instance.default"]
|
||||
wantProvider = addrs.NewLegacyProvider("test")
|
||||
wantProvider = addrs.NewDefaultProvider("test")
|
||||
if !got.Provider.Equals(wantProvider) {
|
||||
t.Fatalf("wrong provider %s, want %s", got.Provider, wantProvider)
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ func TestMergeProviderVersionConstraints(t *testing.T) {
|
|||
VersionConstraints: []VersionConstraint{},
|
||||
},
|
||||
"null": ProviderRequirements{
|
||||
Type: addrs.NewLegacyProvider("null"),
|
||||
Type: addrs.NewDefaultProvider("null"),
|
||||
VersionConstraints: []VersionConstraint{
|
||||
VersionConstraint{
|
||||
Required: version.Constraints(nil),
|
||||
|
@ -292,7 +292,7 @@ func TestMergeProviderVersionConstraints(t *testing.T) {
|
|||
},
|
||||
map[string]ProviderRequirements{
|
||||
"random": ProviderRequirements{
|
||||
Type: addrs.NewLegacyProvider("random"),
|
||||
Type: addrs.NewDefaultProvider("random"),
|
||||
VersionConstraints: []VersionConstraint{vc2},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -33,6 +33,18 @@ func TestNewModule_provider_local_name(t *testing.T) {
|
|||
if localName != "nonexist" {
|
||||
t.Error("wrong local name returned for a non-local provider")
|
||||
}
|
||||
|
||||
// can also look up the "terraform" provider and see that it sources is
|
||||
// allowed to be overridden, even though there is a builtin provider
|
||||
// called "terraform".
|
||||
p = addrs.NewProvider(addrs.DefaultRegistryHost, "not-builtin", "not-terraform")
|
||||
if name, exists := mod.ProviderLocalNames[p]; !exists {
|
||||
t.Fatal("provider FQN not-builtin/not-terraform not found")
|
||||
} else {
|
||||
if name != "terraform" {
|
||||
t.Fatalf("provider localname mismatch: got %s, want terraform", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test validates the provider FQNs set in each Resource
|
||||
|
@ -45,7 +57,7 @@ func TestNewModule_resource_providers(t *testing.T) {
|
|||
// both the root and child module have two resources, one which should use
|
||||
// the default implied provider and one explicitly using a provider set in
|
||||
// required_providers
|
||||
wantImplicit := addrs.NewLegacyProvider("test")
|
||||
wantImplicit := addrs.NewDefaultProvider("test")
|
||||
wantFoo := addrs.NewProvider(addrs.DefaultRegistryHost, "foo", "test")
|
||||
wantBar := addrs.NewProvider(addrs.DefaultRegistryHost, "bar", "test")
|
||||
|
||||
|
|
|
@ -92,6 +92,15 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost
|
|||
}
|
||||
}
|
||||
|
||||
case "required_providers":
|
||||
// required_providers should be nested inside a "terraform" block
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid required_providers block",
|
||||
Detail: "A \"required_providers\" block must be nested inside a \"terraform\" block.",
|
||||
Subject: block.TypeRange.Ptr(),
|
||||
})
|
||||
|
||||
case "provider":
|
||||
cfg, cfgDiags := decodeProviderBlock(block)
|
||||
diags = append(diags, cfgDiags...)
|
||||
|
@ -193,6 +202,12 @@ var configFileSchema = &hcl.BodySchema{
|
|||
{
|
||||
Type: "terraform",
|
||||
},
|
||||
{
|
||||
// This one is not really valid, but we include it here so we
|
||||
// can create a specialized error message hinting the user to
|
||||
// nest it inside a "terraform" block.
|
||||
Type: "required_providers",
|
||||
},
|
||||
{
|
||||
Type: "provider",
|
||||
LabelNames: []string{"name"},
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# A top-level required_providers block is not valid, but we have a specialized
|
||||
# error for it to hint the user to move it into a terraform block.
|
||||
required_providers { # ERROR: Invalid required_providers block
|
||||
}
|
||||
|
||||
# This one is valid, and what the user should rewrite the above to be like.
|
||||
terraform {
|
||||
required_providers {}
|
||||
}
|
|
@ -19,3 +19,10 @@ resource "implied_foo" "bar" {
|
|||
module "child" {
|
||||
source = "./child"
|
||||
}
|
||||
|
||||
# There is no provider in required_providers called "terraform", but for
|
||||
# this name in particular we imply terraform.io/builtin/terraform instead,
|
||||
# to avoid selecting the now-unmaintained
|
||||
# registry.terraform.io/hashicorp/terraform.
|
||||
data "terraform_remote_state" "bar" {
|
||||
}
|
||||
|
|
|
@ -4,5 +4,8 @@ terraform {
|
|||
foo-test = {
|
||||
source = "foo/test"
|
||||
}
|
||||
terraform = {
|
||||
source = "not-builtin/not-terraform"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6
go.mod
6
go.mod
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible
|
||||
github.com/apparentlymart/go-cidr v1.0.1
|
||||
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0
|
||||
github.com/apparentlymart/go-userdirs v0.0.0-20190512014041-4a23807e62b9
|
||||
github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
|
||||
|
@ -127,11 +128,12 @@ require (
|
|||
go.uber.org/atomic v1.3.2 // indirect
|
||||
go.uber.org/multierr v1.1.0 // indirect
|
||||
go.uber.org/zap v1.9.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||
golang.org/x/mod v0.2.0
|
||||
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
|
||||
google.golang.org/api v0.9.0
|
||||
google.golang.org/grpc v1.21.1
|
||||
gopkg.in/ini.v1 v1.42.0 // indirect
|
||||
|
|
15
go.sum
15
go.sum
|
@ -70,6 +70,8 @@ github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFU
|
|||
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||
github.com/apparentlymart/go-userdirs v0.0.0-20190512014041-4a23807e62b9 h1:GRMI604e1ILyP9b5DTNAZFHx+Vu693kxb9ZBrIA2JQg=
|
||||
github.com/apparentlymart/go-userdirs v0.0.0-20190512014041-4a23807e62b9/go.mod h1:7kfpUbyCdGJ9fDRCp3fopPQi5+cKNHgTE4ZuNrO71Cw=
|
||||
github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171 h1:19Seu/H5gq3Ugtx+CGenwF89SDG3S1REX5i6PJj3RK4=
|
||||
github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171/go.mod h1:JXY95WvQrPJQtudvNARshgWajS7jNNlM90altXIPNyI=
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs=
|
||||
|
@ -431,8 +433,6 @@ github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw=
|
|||
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zclconf/go-cty v1.2.0 h1:sPHsy7ADcIZQP3vILvTjrh74ZA175TFP5vqiNK1UmlI=
|
||||
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||
github.com/zclconf/go-cty v1.3.0 h1:ig1G6+rJHX6jZDRjw4LUD3J8q7SBAagcmbM7bQ8ijmI=
|
||||
github.com/zclconf/go-cty v1.3.0/go.mod h1:YO23e2L18AG+ZYQfSobnY4G65nvwvprPCxBHkufUH1k=
|
||||
github.com/zclconf/go-cty v1.3.1 h1:QIOZl+CKKdkv4l2w3lG23nNzXgLoxsWLSEdg1MlX4p0=
|
||||
github.com/zclconf/go-cty v1.3.1/go.mod h1:YO23e2L18AG+ZYQfSobnY4G65nvwvprPCxBHkufUH1k=
|
||||
github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=
|
||||
|
@ -452,8 +452,8 @@ golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnf
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
|
@ -463,6 +463,8 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
|
|||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -504,6 +506,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
|
||||
|
@ -526,6 +529,10 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
|
|||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 h1:Dh6fw+p6FyRl5x/FvNswO1ji0lIGzm3KP8Y9VkS9PTE=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
|
|
|
@ -55,7 +55,7 @@ func shimNewState(newState *states.State, providers map[string]terraform.Resourc
|
|||
for key, i := range res.Instances {
|
||||
resState := &terraform.ResourceState{
|
||||
Type: resType,
|
||||
Provider: res.ProviderConfig.LegacyString(),
|
||||
Provider: legacyProviderConfigString(res.ProviderConfig),
|
||||
}
|
||||
|
||||
// We should always have a Current instance here, but be safe about checking.
|
||||
|
@ -186,3 +186,37 @@ func shimmedAttributes(instance *states.ResourceInstanceObjectSrc, res *schema.R
|
|||
|
||||
return instanceState.Attributes, nil
|
||||
}
|
||||
|
||||
func shimLegacyState(legacy *terraform.State) (*states.State, error) {
|
||||
state, err := terraform.ShimLegacyState(legacy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if state.HasResources() {
|
||||
for _, module := range state.Modules {
|
||||
for name, resource := range module.Resources {
|
||||
module.Resources[name].ProviderConfig.Provider = addrs.ImpliedProviderForUnqualifiedType(resource.Addr.Resource.ImpliedProvider())
|
||||
}
|
||||
}
|
||||
}
|
||||
return state, err
|
||||
}
|
||||
|
||||
// legacyProviderConfigString was copied from addrs.Provider.LegacyString() to
|
||||
// create a legacy-style string from a non-legacy provider. This is only
|
||||
// necessary as this package shims back and forth between legacy and modern
|
||||
// state, neither of which encode the addrs.Provider for a resource.
|
||||
func legacyProviderConfigString(pc addrs.AbsProviderConfig) string {
|
||||
if pc.Alias != "" {
|
||||
if len(pc.Module) == 0 {
|
||||
return fmt.Sprintf("%s.%s.%s", "provider", pc.Provider.Type, pc.Alias)
|
||||
} else {
|
||||
return fmt.Sprintf("%s.%s.%s.%s", pc.Module.String(), "provider", pc.Provider.LegacyString(), pc.Alias)
|
||||
}
|
||||
}
|
||||
if len(pc.Module) == 0 {
|
||||
return fmt.Sprintf("%s.%s", "provider", pc.Provider.Type)
|
||||
}
|
||||
return fmt.Sprintf("%s.%s.%s", pc.Module.String(), "provider", pc.Provider.Type)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ func TestStateShim(t *testing.T) {
|
|||
},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -58,7 +58,7 @@ func TestStateShim(t *testing.T) {
|
|||
DependsOn: []addrs.Referenceable{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -77,7 +77,7 @@ func TestStateShim(t *testing.T) {
|
|||
DependsOn: []addrs.Referenceable{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -101,7 +101,7 @@ func TestStateShim(t *testing.T) {
|
|||
},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -127,7 +127,7 @@ func TestStateShim(t *testing.T) {
|
|||
},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -144,7 +144,7 @@ func TestStateShim(t *testing.T) {
|
|||
DependsOn: []addrs.Referenceable{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -160,7 +160,7 @@ func TestStateShim(t *testing.T) {
|
|||
DependsOn: []addrs.Referenceable{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -177,7 +177,7 @@ func TestStateShim(t *testing.T) {
|
|||
DependsOn: []addrs.Referenceable{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
@ -332,3 +332,92 @@ func TestStateShim(t *testing.T) {
|
|||
t.Fatalf("wrong result state\ngot:\n%s\n\nwant:\n%s", shimmed, expected)
|
||||
}
|
||||
}
|
||||
|
||||
// TestShimLegacyState only checks the functionality unique to this func: adding
|
||||
// the implied provider FQN
|
||||
func TestShimLegacyState(t *testing.T) {
|
||||
|
||||
input := &terraform.State{
|
||||
Version: 3,
|
||||
Modules: []*terraform.ModuleState{
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root"},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_thing.baz": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "baz",
|
||||
Attributes: map[string]string{
|
||||
"id": "baz",
|
||||
"bazzle": "dazzle",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root", "child"},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_thing.bar": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "module.child.provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "bar",
|
||||
Attributes: map[string]string{
|
||||
"id": "bar",
|
||||
"fizzle": "wizzle",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expected := states.NewState()
|
||||
root := expected.EnsureModule(addrs.RootModuleInstance)
|
||||
root.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.NoKey),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsFlat: map[string]string{"id": "baz", "bazzle": "dazzle"},
|
||||
DependsOn: []addrs.Referenceable{},
|
||||
Dependencies: []addrs.ConfigResource{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
child := expected.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
|
||||
child.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "bar",
|
||||
}.Instance(addrs.NoKey),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsFlat: map[string]string{"id": "bar", "fizzle": "wizzle"},
|
||||
DependsOn: []addrs.Referenceable{},
|
||||
Dependencies: []addrs.ConfigResource{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: child.Addr.Module(),
|
||||
},
|
||||
)
|
||||
|
||||
got, err := shimLegacyState(input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if !got.Equal(expected) {
|
||||
t.Fatal("wrong result")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -481,10 +481,19 @@ func Test(t TestT, c TestCase) {
|
|||
c.PreCheck()
|
||||
}
|
||||
|
||||
providerFactories, err := testProviderFactories(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// get instances of all providers, so we can use the individual
|
||||
// resources to shim the state during the tests.
|
||||
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()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -492,12 +501,7 @@ func Test(t TestT, c TestCase) {
|
|||
providers[name] = p
|
||||
}
|
||||
|
||||
providerResolver, err := testProviderResolver(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
opts := terraform.ContextOpts{ProviderResolver: providerResolver}
|
||||
opts := terraform.ContextOpts{Providers: providerFactories}
|
||||
|
||||
// A single state variable to track the lifecycle, starting with no state
|
||||
var state *terraform.State
|
||||
|
@ -650,10 +654,14 @@ func testProviderConfig(c TestCase) string {
|
|||
return strings.Join(lines, "")
|
||||
}
|
||||
|
||||
// testProviderFactories combines the fixed Providers and
|
||||
// ResourceProviderFactory functions into a single map of
|
||||
// ResourceProviderFactory functions.
|
||||
func testProviderFactories(c TestCase) map[string]terraform.ResourceProviderFactory {
|
||||
// testProviderFactoriesLegacy is like testProviderFactories but it returns
|
||||
// providers implementing the legacy interface terraform.ResourceProvider,
|
||||
// rather than the current providers.Interface.
|
||||
//
|
||||
// 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)
|
||||
for k, pf := range c.ProviderFactories {
|
||||
ctxProviders[k] = pf
|
||||
|
@ -663,24 +671,25 @@ func testProviderFactories(c TestCase) map[string]terraform.ResourceProviderFact
|
|||
for k, p := range c.Providers {
|
||||
ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p)
|
||||
}
|
||||
return ctxProviders
|
||||
return ctxProviders, nil
|
||||
}
|
||||
|
||||
// testProviderResolver is a helper to build a ResourceProviderResolver
|
||||
// with pre instantiated ResourceProviders, so that we can reset them for the
|
||||
// test, while only calling the factory function once.
|
||||
// Any errors are stored so that they can be returned by the factory in
|
||||
// terraform to match non-test behavior.
|
||||
func testProviderResolver(c TestCase) (providers.Resolver, error) {
|
||||
ctxProviders := testProviderFactories(c)
|
||||
// testProviderFactories combines the fixed Providers and
|
||||
// ResourceProviderFactory functions into a single map of
|
||||
// ResourceProviderFactory functions.
|
||||
func testProviderFactories(c TestCase) (map[addrs.Provider]providers.Factory, error) {
|
||||
ctxProviders, err := testProviderFactoriesLegacy(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// wrap the old provider factories in the test grpc server so they can be
|
||||
// called from terraform.
|
||||
// We additionally wrap all of the factories as a GRPCTestProvider, which
|
||||
// allows them to appear as a new-style providers.Interface, rather than
|
||||
// the legacy terraform.ResourceProvider.
|
||||
newProviders := make(map[addrs.Provider]providers.Factory)
|
||||
|
||||
for k, pf := range ctxProviders {
|
||||
for legacyName, pf := range ctxProviders {
|
||||
factory := pf // must copy to ensure each closure sees its own value
|
||||
newProviders[addrs.NewLegacyProvider(k)] = func() (providers.Interface, error) {
|
||||
newProviders[addrs.NewDefaultProvider(legacyName)] = func() (providers.Interface, error) {
|
||||
p, err := factory()
|
||||
if err != nil {
|
||||
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
|
||||
|
|
|
@ -43,7 +43,7 @@ func testStep(opts terraform.ContextOpts, state *terraform.State, step TestStep)
|
|||
|
||||
// Build the context
|
||||
opts.Config = cfg
|
||||
opts.State, err = terraform.ShimLegacyState(state)
|
||||
opts.State, err = shimLegacyState(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -14,9 +14,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/plugin/discovery"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
|
@ -1036,77 +1033,6 @@ func TestTest_Taint(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTestProviderResolver(t *testing.T) {
|
||||
stubProvider := func(name string) terraform.ResourceProvider {
|
||||
return &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
name: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
c := TestCase{
|
||||
ProviderFactories: map[string]terraform.ResourceProviderFactory{
|
||||
"foo": terraform.ResourceProviderFactoryFixed(stubProvider("foo")),
|
||||
"bar": terraform.ResourceProviderFactoryFixed(stubProvider("bar")),
|
||||
},
|
||||
Providers: map[string]terraform.ResourceProvider{
|
||||
"baz": stubProvider("baz"),
|
||||
"bop": stubProvider("bop"),
|
||||
},
|
||||
}
|
||||
|
||||
resolver, err := testProviderResolver(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
reqd := discovery.PluginRequirements{
|
||||
"foo": &discovery.PluginConstraints{},
|
||||
"bar": &discovery.PluginConstraints{},
|
||||
"baz": &discovery.PluginConstraints{},
|
||||
"bop": &discovery.PluginConstraints{},
|
||||
}
|
||||
|
||||
factories, errs := resolver.ResolveProviders(reqd)
|
||||
if len(errs) != 0 {
|
||||
for _, err := range errs {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Fatal("unexpected errors")
|
||||
}
|
||||
|
||||
for name := range reqd {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
pf, ok := factories[addrs.NewLegacyProvider(name)]
|
||||
if !ok {
|
||||
t.Fatalf("no factory for %q", name)
|
||||
}
|
||||
p, err := pf()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp := p.GetSchema()
|
||||
_, ok = resp.Provider.Block.Attributes[name]
|
||||
if !ok {
|
||||
var has string
|
||||
for k := range resp.Provider.Block.Attributes {
|
||||
has = k
|
||||
break
|
||||
}
|
||||
if has != "" {
|
||||
t.Errorf("provider %q does not have the expected schema attribute %q (but has %q)", name, name, has)
|
||||
} else {
|
||||
t.Errorf("provider %q does not have the expected schema attribute %q", name, name)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const testConfigStr = `
|
||||
resource "test_instance" "foo" {}
|
||||
`
|
||||
|
|
|
@ -105,7 +105,7 @@ func (c *Config) addProviderRequirements(reqs getproviders.Requirements) tfdiags
|
|||
fqn = addr
|
||||
}
|
||||
if fqn.IsZero() {
|
||||
fqn = addrs.NewDefaultProvider(localName)
|
||||
fqn = addrs.ImpliedProviderForUnqualifiedType(localName)
|
||||
}
|
||||
if _, ok := reqs[fqn]; !ok {
|
||||
// We'll at least have an unconstrained dependency then, but might
|
||||
|
|
|
@ -145,3 +145,24 @@ func (err ErrQueryFailed) Error() string {
|
|||
func (err ErrQueryFailed) Unwrap() error {
|
||||
return err.Wrapped
|
||||
}
|
||||
|
||||
// ErrIsNotExist returns true if and only if the given error is one of the
|
||||
// errors from this package that represents an affirmative response that a
|
||||
// requested object does not exist.
|
||||
//
|
||||
// This is as opposed to errors indicating that the source is unavailable
|
||||
// or misconfigured in some way, where we therefore cannot say for certain
|
||||
// whether the requested object exists.
|
||||
//
|
||||
// If a caller needs to take a special action based on something not existing,
|
||||
// such as falling back on some other source, use this function rather than
|
||||
// direct type assertions so that the set of possible "not exist" errors can
|
||||
// grow in future.
|
||||
func ErrIsNotExist(err error) bool {
|
||||
switch err.(type) {
|
||||
case ErrProviderNotKnown, ErrPlatformNotSupported:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
package getproviders
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
)
|
||||
|
||||
// MockSource is an in-memory-only, statically-configured source intended for
|
||||
// use only in unit tests of other subsystems that consume provider sources.
|
||||
//
|
||||
// The MockSource also tracks calls to it in case a calling test wishes to
|
||||
// assert that particular calls were made.
|
||||
//
|
||||
// This should not be used outside of unit test code.
|
||||
type MockSource struct {
|
||||
packages []PackageMeta
|
||||
calls [][]interface{}
|
||||
}
|
||||
|
||||
var _ Source = (*MockSource)(nil)
|
||||
|
||||
// NewMockSource creates and returns a MockSource with the given packages.
|
||||
//
|
||||
// The given packages don't necessarily need to refer to objects that actually
|
||||
// exist on disk or over the network, unless the calling test is planning to
|
||||
// use (directly or indirectly) the results for further provider installation
|
||||
// actions.
|
||||
func NewMockSource(packages []PackageMeta) *MockSource {
|
||||
return &MockSource{
|
||||
packages: packages,
|
||||
}
|
||||
}
|
||||
|
||||
// AvailableVersions returns all of the versions of the given provider that
|
||||
// are available in the fixed set of packages that were passed to
|
||||
// NewMockSource when creating the receiving source.
|
||||
func (s *MockSource) AvailableVersions(provider addrs.Provider) (VersionList, error) {
|
||||
s.calls = append(s.calls, []interface{}{"AvailableVersions", provider})
|
||||
var ret VersionList
|
||||
for _, pkg := range s.packages {
|
||||
if pkg.Provider == provider {
|
||||
ret = append(ret, pkg.Version)
|
||||
}
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
// In this case, we'll behave like a registry that doesn't know about
|
||||
// this provider at all, rather than just returning an empty result.
|
||||
return nil, ErrProviderNotKnown{provider}
|
||||
}
|
||||
ret.Sort()
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// PackageMeta returns the first package from the list given to NewMockSource
|
||||
// when creating the receiver that has the given provider, version, and
|
||||
// target platform.
|
||||
//
|
||||
// If none of the packages match, it returns ErrPlatformNotSupported to
|
||||
// simulate the situation where a provider release isn't available for a
|
||||
// particular platform.
|
||||
//
|
||||
// Note that if the list of packages passed to NewMockSource contains more
|
||||
// than one with the same provider, version, and target this function will
|
||||
// always return the first one in the list, which may not match the behavior
|
||||
// of other sources in an equivalent situation because it's a degenerate case
|
||||
// with undefined results.
|
||||
func (s *MockSource) PackageMeta(provider addrs.Provider, version Version, target Platform) (PackageMeta, error) {
|
||||
s.calls = append(s.calls, []interface{}{"PackageMeta", provider, version, target})
|
||||
|
||||
for _, pkg := range s.packages {
|
||||
if pkg.Provider != provider {
|
||||
continue
|
||||
}
|
||||
if pkg.Version != version {
|
||||
// (We're using strict equality rather than precedence here,
|
||||
// because this is an exact version specification. The caller
|
||||
// should consider precedence when selecting a version in the
|
||||
// AvailableVersions response, and pass the exact selected
|
||||
// version here.)
|
||||
continue
|
||||
}
|
||||
if pkg.TargetPlatform != target {
|
||||
continue
|
||||
}
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
// If we fall out here then nothing matched at all, so we'll treat that
|
||||
// as "platform not supported" for consistency with RegistrySource.
|
||||
return PackageMeta{}, ErrPlatformNotSupported{
|
||||
Provider: provider,
|
||||
Version: version,
|
||||
Platform: target,
|
||||
}
|
||||
}
|
||||
|
||||
// CallLog returns a list of calls to other methods of the receiever that have
|
||||
// been called since it was created, in case a calling test wishes to verify
|
||||
// a particular sequence of operations.
|
||||
//
|
||||
// The result is a slice of slices where the first element of each inner slice
|
||||
// is the name of the method that was called, and then any subsequent elements
|
||||
// are positional arguments passed to that method.
|
||||
//
|
||||
// Callers are forbidden from modifying any objects accessible via the returned
|
||||
// value.
|
||||
func (s *MockSource) CallLog() [][]interface{} {
|
||||
return s.calls
|
||||
}
|
||||
|
||||
// FakePackageMeta constructs and returns a PackageMeta that carries the given
|
||||
// metadata but has fake location information that is likely to fail if
|
||||
// attempting to install from it.
|
||||
func FakePackageMeta(provider addrs.Provider, version Version, target Platform) PackageMeta {
|
||||
return PackageMeta{
|
||||
Provider: provider,
|
||||
Version: version,
|
||||
TargetPlatform: target,
|
||||
|
||||
// Some fake but somewhat-realistic-looking other metadata. This
|
||||
// points nowhere, so will fail if attempting to actually use it.
|
||||
Filename: fmt.Sprintf("terraform-provider-%s_%s_%s.zip", provider.Type, version.String(), target.String()),
|
||||
Location: PackageHTTPURL(fmt.Sprintf("https://fake.invalid/terraform-provider-%s_%s.zip", provider.Type, version.String())),
|
||||
}
|
||||
}
|
||||
|
||||
// FakeInstallablePackageMeta constructs and returns a PackageMeta that points
|
||||
// to a temporary archive file that could actually be installed in principle.
|
||||
//
|
||||
// Installing it will not produce a working provider though: just a fake file
|
||||
// posing as an executable.
|
||||
//
|
||||
// It's the caller's responsibility to call the close callback returned
|
||||
// alongside the result in order to clean up the temporary file. The caller
|
||||
// should call the callback even if this function returns an error, because
|
||||
// some error conditions leave a partially-created file on disk.
|
||||
func FakeInstallablePackageMeta(provider addrs.Provider, version Version, target Platform) (PackageMeta, func(), error) {
|
||||
f, err := ioutil.TempFile("", "terraform-getproviders-fake-package-")
|
||||
if err != nil {
|
||||
return PackageMeta{}, func() {}, err
|
||||
}
|
||||
|
||||
// After this point, all of our return paths should include this as the
|
||||
// close callback.
|
||||
close := func() {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
}
|
||||
|
||||
execFilename := fmt.Sprintf("terraform-provider-%s_%s", provider.Type, version.String())
|
||||
if target.OS == "windows" {
|
||||
// For a little more (technically unnecessary) realism...
|
||||
execFilename += ".exe"
|
||||
}
|
||||
|
||||
zw := zip.NewWriter(f)
|
||||
fw, err := zw.Create(execFilename)
|
||||
if err != nil {
|
||||
return PackageMeta{}, close, fmt.Errorf("failed to add %s to mock zip file: %s", execFilename, err)
|
||||
}
|
||||
fmt.Fprintf(fw, "This is a fake provider package for %s %s, not a real provider.\n", provider, version)
|
||||
err = zw.Close()
|
||||
if err != nil {
|
||||
return PackageMeta{}, close, fmt.Errorf("failed to close the mock zip file: %s", err)
|
||||
}
|
||||
|
||||
meta := PackageMeta{
|
||||
Provider: provider,
|
||||
Version: version,
|
||||
TargetPlatform: target,
|
||||
|
||||
Location: PackageLocalArchive(f.Name()),
|
||||
|
||||
// This is a fake filename that mimics what a real registry might
|
||||
// indicate as a good filename for this package, in case some caller
|
||||
// intends to use it to name a local copy of the temporary file.
|
||||
// (At the time of writing, no caller actually does that, but who
|
||||
// knows what the future holds?)
|
||||
Filename: fmt.Sprintf("terraform-provider-%s_%s_%s.zip", provider.Type, version.String(), target.String()),
|
||||
}
|
||||
return meta, close, nil
|
||||
}
|
|
@ -2,7 +2,6 @@ package getproviders
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
svchost "github.com/hashicorp/terraform-svchost"
|
||||
|
@ -34,19 +33,68 @@ func (s MultiSource) AvailableVersions(provider addrs.Provider) (VersionList, er
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
panic("MultiSource.AvailableVersions not yet implemented")
|
||||
// We will return the union of all versions reported by the nested
|
||||
// sources that have matching patterns that accept the given provider.
|
||||
vs := make(map[Version]struct{})
|
||||
for _, selector := range s {
|
||||
if !selector.CanHandleProvider(provider) {
|
||||
continue // doesn't match the given patterns
|
||||
}
|
||||
thisSourceVersions, err := selector.Source.AvailableVersions(provider)
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
// okay
|
||||
case ErrProviderNotKnown:
|
||||
continue // ignore, then
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range thisSourceVersions {
|
||||
vs[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(vs) == 0 {
|
||||
return nil, ErrProviderNotKnown{provider}
|
||||
}
|
||||
ret := make(VersionList, 0, len(vs))
|
||||
for v := range vs {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
ret.Sort()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// PackageMeta retrieves the package metadata for the given provider from the
|
||||
// first selector that indicates support for it.
|
||||
// PackageMeta retrieves the package metadata for the requested provider package
|
||||
// from the first selector that indicates availability of it.
|
||||
func (s MultiSource) PackageMeta(provider addrs.Provider, version Version, target Platform) (PackageMeta, error) {
|
||||
if len(s) == 0 { // Easy case: no providers exist at all
|
||||
return PackageMeta{}, ErrProviderNotKnown{provider}
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
panic("MultiSource.PackageMeta not yet implemented")
|
||||
for _, selector := range s {
|
||||
if !selector.CanHandleProvider(provider) {
|
||||
continue // doesn't match the given patterns
|
||||
}
|
||||
meta, err := selector.Source.PackageMeta(provider, version, target)
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
return meta, nil
|
||||
case ErrProviderNotKnown, ErrPlatformNotSupported:
|
||||
continue // ignore, then
|
||||
default:
|
||||
return PackageMeta{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// If we fall out here then none of the sources have the requested
|
||||
// package.
|
||||
return PackageMeta{}, ErrPlatformNotSupported{
|
||||
Provider: provider,
|
||||
Version: version,
|
||||
Platform: target,
|
||||
}
|
||||
}
|
||||
|
||||
// MultiSourceSelector is an element of the source selection configuration on
|
||||
|
@ -103,10 +151,10 @@ func ParseMultiSourceMatchingPatterns(strs []string) (MultiSourceMatchingPattern
|
|||
parts = parts[1:]
|
||||
}
|
||||
|
||||
if !validProviderNamePattern.MatchString(parts[1]) {
|
||||
if !validProviderNameOrWildcard(parts[1]) {
|
||||
return nil, fmt.Errorf("invalid provider type %q in provider matching pattern %q: must either be the wildcard * or a provider type name", parts[1], str)
|
||||
}
|
||||
if !validProviderNamePattern.MatchString(parts[0]) {
|
||||
if !validProviderNameOrWildcard(parts[0]) {
|
||||
return nil, fmt.Errorf("invalid registry namespace %q in provider matching pattern %q: must either be the wildcard * or a literal namespace", parts[1], str)
|
||||
}
|
||||
|
||||
|
@ -165,6 +213,12 @@ const Wildcard string = "*"
|
|||
// We'll read the default registry host from over in the addrs package, to
|
||||
// avoid duplicating it. A "default" provider uses the default registry host
|
||||
// by definition.
|
||||
var defaultRegistryHost = addrs.NewDefaultProvider("placeholder").Hostname
|
||||
var defaultRegistryHost = addrs.DefaultRegistryHost
|
||||
|
||||
var validProviderNamePattern = regexp.MustCompile("^[a-zA-Z0-9_-]+|\\*$")
|
||||
func validProviderNameOrWildcard(s string) bool {
|
||||
if s == Wildcard {
|
||||
return true
|
||||
}
|
||||
_, err := addrs.ParseProviderPart(s)
|
||||
return err == nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
package providercache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
)
|
||||
|
@ -31,3 +37,73 @@ type CachedProvider struct {
|
|||
// within a particular path string.
|
||||
ExecutableFile string
|
||||
}
|
||||
|
||||
// Hash computes a hash of the contents of the package directory associated
|
||||
// with the receiving cached provider, using whichever hash algorithm is
|
||||
// the current default.
|
||||
//
|
||||
// Currently, this method returns version 1 hashes as produced by the
|
||||
// method HashV1, but this function may switch to other versions in later
|
||||
// releases. Call HashV1 directly if you specifically need a V1 hash.
|
||||
func (cp *CachedProvider) Hash() (string, error) {
|
||||
return cp.HashV1()
|
||||
}
|
||||
|
||||
// MatchesHash returns true if the package on disk matches the given hash,
|
||||
// or false otherwise. If it cannot traverse the package directory and read
|
||||
// all of the files in it, or if the hash is in an unsupported format,
|
||||
// CheckHash returns an error.
|
||||
//
|
||||
// There is currently only one hash format, as implemented by HashV1. However,
|
||||
// if others are introduced in future MatchesHash may accept multiple formats,
|
||||
// and may generate errors for any formats that become obsolete.
|
||||
func (cp *CachedProvider) MatchesHash(want string) (bool, error) {
|
||||
switch {
|
||||
case strings.HasPrefix(want, "h1"):
|
||||
got, err := cp.HashV1()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return got == want, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unsupported hash format (this may require a newer version of Terraform)")
|
||||
}
|
||||
}
|
||||
|
||||
// HashV1 computes a hash of the contents of the package directory associated
|
||||
// with the receiving cached provider using hash algorithm 1.
|
||||
//
|
||||
// The hash covers the paths to files in the directory and the contents of
|
||||
// those files. It does not cover other metadata about the files, such as
|
||||
// permissions.
|
||||
//
|
||||
// This function is named "HashV1" in anticipation of other hashing algorithms
|
||||
// being added (in a backward-compatible way) in future. The result from
|
||||
// HashV1 always begins with the prefix "h1:" so that callers can distinguish
|
||||
// the results of potentially multiple different hash algorithms in future.
|
||||
func (cp *CachedProvider) HashV1() (string, error) {
|
||||
// Our HashV1 is really just the Go Modules hash version 1, which is
|
||||
// sufficient for our needs and already well-used for identity of
|
||||
// Go Modules distribution packages. It is also blocked from incompatible
|
||||
// changes by being used in a wide array of go.sum files already.
|
||||
//
|
||||
// In particular, it also supports computing an equivalent hash from
|
||||
// an unpacked zip file, which is not important for Terraform workflow
|
||||
// today but is likely to become so in future if we adopt a top-level
|
||||
// lockfile mechanism that is intended to be checked in to version control,
|
||||
// rather than just a transient lock for a particular local cache directory.
|
||||
// (In that case we'd need to check hashes of _packed_ packages, too.)
|
||||
|
||||
// We'll first dereference a possible symlink at our PackageDir location,
|
||||
// as would be created if this package were linked in from another cache.
|
||||
packageDir, err := filepath.EvalSymlinks(cp.PackageDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Internally, dirhash.Hash1 produces a string containing a sequence of
|
||||
// newline-separated path+filehash pairs for all of the files in the
|
||||
// directory, and then finally produces a hash of that string to return.
|
||||
// In both cases, the hash algorithm is SHA256.
|
||||
return dirhash.HashDir(packageDir, "", dirhash.Hash1)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package providercache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
)
|
||||
|
||||
func TestCachedProviderHash(t *testing.T) {
|
||||
cp := &CachedProvider{
|
||||
Provider: addrs.NewProvider(
|
||||
addrs.DefaultRegistryHost,
|
||||
"hashicorp", "null",
|
||||
),
|
||||
Version: getproviders.MustParseVersion("2.0.0"),
|
||||
|
||||
PackageDir: "testdata/cachedir/registry.terraform.io/hashicorp/null/2.0.0/darwin_amd64",
|
||||
ExecutableFile: "testdata/cachedir/registry.terraform.io/hashicorp/null/2.0.0/darwin_amd64/terraform-provider-null",
|
||||
}
|
||||
|
||||
want := "h1:qjsREM4DqEWECD43FcPqddZ9oxCG+IaMTxvWPciS05g="
|
||||
got, err := cp.Hash()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if got != want {
|
||||
t.Errorf("wrong Hash result\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
gotMatches, err := cp.MatchesHash(want)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if wantMatches := true; gotMatches != wantMatches {
|
||||
t.Errorf("wrong MatchesHash result\ngot: %#v\nwant: %#v", gotMatches, wantMatches)
|
||||
}
|
||||
|
||||
// The windows build has a different hash because its executable filename
|
||||
// has a .exe suffix, but the darwin build (hashed above) does not.
|
||||
cp2 := &CachedProvider{
|
||||
Provider: addrs.NewProvider(
|
||||
addrs.DefaultRegistryHost,
|
||||
"hashicorp", "null",
|
||||
),
|
||||
Version: getproviders.MustParseVersion("2.0.0"),
|
||||
|
||||
PackageDir: "testdata/cachedir/registry.terraform.io/hashicorp/null/2.0.0/windows_amd64",
|
||||
ExecutableFile: "testdata/cachedir/registry.terraform.io/hashicorp/null/2.0.0/windows_amd64/terraform-provider-null",
|
||||
}
|
||||
gotMatches, err = cp2.MatchesHash(want)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if wantMatches := false; gotMatches != wantMatches {
|
||||
t.Errorf("wrong MatchesHash result for other package\ngot: %#v\nwant: %#v", gotMatches, wantMatches)
|
||||
}
|
||||
|
||||
}
|
|
@ -4,10 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/copydir"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
)
|
||||
|
||||
|
@ -22,6 +19,10 @@ func (d *Dir) InstallPackage(ctx context.Context, meta getproviders.PackageMeta)
|
|||
d.baseDir, meta.Provider, meta.Version, d.targetPlatform,
|
||||
)
|
||||
|
||||
// Invalidate our metaCache so that subsequent read calls will re-scan to
|
||||
// incorporate any changes we make here.
|
||||
d.metaCache = nil
|
||||
|
||||
log.Printf("[TRACE] providercache.Dir.InstallPackage: installing %s v%s from %s", meta.Provider, meta.Version, meta.Location)
|
||||
switch location := meta.Location.(type) {
|
||||
case getproviders.PackageHTTPURL:
|
||||
|
@ -54,71 +55,12 @@ func (d *Dir) LinkFromOtherCache(entry *CachedProvider) error {
|
|||
currentPath := entry.PackageDir
|
||||
log.Printf("[TRACE] providercache.Dir.LinkFromOtherCache: linking %s v%s from existing cache %s to %s", entry.Provider, entry.Version, currentPath, newPath)
|
||||
|
||||
absNew, err := filepath.Abs(newPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make new path %s absolute: %s", newPath, err)
|
||||
}
|
||||
absCurrent, err := filepath.Abs(currentPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make existing cache path %s absolute: %s", currentPath, err)
|
||||
}
|
||||
|
||||
// Before we do anything else, we'll do a quick check to make sure that
|
||||
// these two paths are not pointing at the same physical directory on
|
||||
// disk. This compares the files by their OS-level device and directory
|
||||
// entry identifiers, not by their virtual filesystem paths.
|
||||
if same, err := copydir.SameFile(absNew, absCurrent); same {
|
||||
return fmt.Errorf("cannot link existing cache path %s to itself", newPath)
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to determine if %s and %s are the same: %s", currentPath, newPath, err)
|
||||
}
|
||||
|
||||
// Invalidate our metaCache so that subsequent read calls will re-scan to
|
||||
// incorporate any changes we make here.
|
||||
d.metaCache = nil
|
||||
|
||||
// Delete anything that's already present at this path first.
|
||||
err = os.RemoveAll(newPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove existing %s before linking it to %s: %s", currentPath, newPath, err)
|
||||
}
|
||||
|
||||
// We'll prefer to create a symlink if possible, but we'll fall back to
|
||||
// a recursive copy if symlink creation fails. It could fail for a number
|
||||
// of reasons, including being on Windows 8 without administrator
|
||||
// privileges or being on a legacy filesystem like FAT that has no way
|
||||
// to represent a symlink. (Generalized symlink support for Windows was
|
||||
// introduced in a Windows 10 minor update.)
|
||||
//
|
||||
// We'd prefer to use a relative path for the symlink to reduce the risk
|
||||
// of it being broken by moving things around later, but we'll fall back
|
||||
// on the absolute path we already calculated if that isn't possible
|
||||
// (e.g. because the two paths are on different "volumes" on an OS with
|
||||
// that concept, like Windows with drive letters and UNC host/share names.)
|
||||
linkTarget, err := filepath.Rel(newPath, absCurrent)
|
||||
if err != nil {
|
||||
linkTarget = absCurrent
|
||||
}
|
||||
|
||||
parentDir := filepath.Dir(absNew)
|
||||
err = os.MkdirAll(parentDir, 0755)
|
||||
if err != nil && os.IsExist(err) {
|
||||
return fmt.Errorf("failed to create parent directories leading to %s: %s", newPath, err)
|
||||
}
|
||||
|
||||
err = os.Symlink(linkTarget, absNew)
|
||||
if err == nil {
|
||||
// Success, then!
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we get down here then symlinking failed and we need a deep copy
|
||||
// instead.
|
||||
err = copydir.CopyDir(absNew, absCurrent)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to either symlink or copy %s to %s: %s", absCurrent, absNew, err)
|
||||
}
|
||||
|
||||
// If we got here then apparently our copy succeeded, so we're done.
|
||||
return nil
|
||||
// We re-use the process of installing from a local directory here, because
|
||||
// the two operations are fundamentally the same: symlink if possible,
|
||||
// deep-copy otherwise.
|
||||
return installFromLocalDir(context.TODO(), currentPath, newPath)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package providercache
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
@ -34,6 +35,12 @@ type Installer struct {
|
|||
// both the disk space and the download time for a particular provider
|
||||
// version between different configurations on the same system.
|
||||
globalCacheDir *Dir
|
||||
|
||||
// builtInProviderTypes is an optional set of types that should be
|
||||
// considered valid to appear in the special terraform.io/builtin/...
|
||||
// namespace, which we use for providers that are built in to Terraform
|
||||
// and thus do not need any separate installation step.
|
||||
builtInProviderTypes []string
|
||||
}
|
||||
|
||||
// NewInstaller constructs and returns a new installer with the given target
|
||||
|
@ -63,12 +70,30 @@ func (i *Installer) SetGlobalCacheDir(cacheDir *Dir) {
|
|||
// A little safety check to catch straightforward mistakes where the
|
||||
// directories overlap. Better to panic early than to do
|
||||
// possibly-distructive actions on the cache directory downstream.
|
||||
if same, err := copydir.SameFile(i.targetDir.baseDir, cacheDir.baseDir); err == nil && !same {
|
||||
panic(fmt.Sprintf("global cache directory %s must not match the installation target directory", i.targetDir.baseDir))
|
||||
if same, err := copydir.SameFile(i.targetDir.baseDir, cacheDir.baseDir); err == nil && same {
|
||||
panic(fmt.Sprintf("global cache directory %s must not match the installation target directory %s", cacheDir.baseDir, i.targetDir.baseDir))
|
||||
}
|
||||
i.globalCacheDir = cacheDir
|
||||
}
|
||||
|
||||
// SetBuiltInProviderTypes tells the receiver to consider the type names in the
|
||||
// given slice to be valid as providers in the special special
|
||||
// terraform.io/builtin/... namespace that we use for providers that are
|
||||
// built in to Terraform and thus do not need a separate installation step.
|
||||
//
|
||||
// If a caller requests installation of a provider in that namespace, the
|
||||
// installer will treat it as a no-op if its name exists in this list, but
|
||||
// will produce an error if it does not.
|
||||
//
|
||||
// The default, if this method isn't called, is for there to be no valid
|
||||
// builtin providers.
|
||||
//
|
||||
// Do not modify the buffer under the given slice after passing it to this
|
||||
// method.
|
||||
func (i *Installer) SetBuiltInProviderTypes(types []string) {
|
||||
i.builtInProviderTypes = types
|
||||
}
|
||||
|
||||
// EnsureProviderVersions compares the given provider requirements with what
|
||||
// is already available in the installer's target directory and then takes
|
||||
// appropriate installation actions to ensure that suitable packages
|
||||
|
@ -112,6 +137,42 @@ func (i *Installer) EnsureProviderVersions(ctx context.Context, reqs getprovider
|
|||
mightNeed := map[addrs.Provider]getproviders.VersionSet{}
|
||||
MightNeedProvider:
|
||||
for provider, versionConstraints := range reqs {
|
||||
if provider.IsBuiltIn() {
|
||||
// Built in providers do not require installation but we'll still
|
||||
// verify that the requested provider name is valid.
|
||||
valid := false
|
||||
for _, name := range i.builtInProviderTypes {
|
||||
if name == provider.Type {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
var err error
|
||||
if valid {
|
||||
if len(versionConstraints) == 0 {
|
||||
// Other than reporting an event for the outcome of this
|
||||
// provider, we'll do nothing else with it: it's just
|
||||
// automatically available for use.
|
||||
if cb := evts.BuiltInProviderAvailable; cb != nil {
|
||||
cb(provider)
|
||||
}
|
||||
} else {
|
||||
// A built-in provider is not permitted to have an explicit
|
||||
// version constraint, because we can only use the version
|
||||
// that is built in to the current Terraform release.
|
||||
err = fmt.Errorf("built-in providers do not support explicit version constraints")
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("this Terraform release has no built-in provider named %q", provider.Type)
|
||||
}
|
||||
if err != nil {
|
||||
errs[provider] = err
|
||||
if cb := evts.BuiltInProviderFailure; cb != nil {
|
||||
cb(provider, err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
acceptableVersions := versions.MeetingConstraints(versionConstraints)
|
||||
if mode.forceQueryAllProviders() {
|
||||
// If our mode calls for us to look for newer versions regardless
|
||||
|
@ -267,6 +328,7 @@ NeedProvider:
|
|||
new := installTo.ProviderVersion(provider, version)
|
||||
if new == nil {
|
||||
err := fmt.Errorf("after installing %s it is still not detected in the target directory; this is a bug in Terraform", provider)
|
||||
errs[provider] = err
|
||||
if cb := evts.FetchPackageFailure; cb != nil {
|
||||
cb(provider, version, err)
|
||||
}
|
||||
|
@ -292,6 +354,41 @@ NeedProvider:
|
|||
}
|
||||
}
|
||||
|
||||
// We'll remember our selections in a lock file inside the target directory,
|
||||
// so callers can recover those exact selections later by calling
|
||||
// SelectedPackages on the same installer.
|
||||
lockEntries := map[addrs.Provider]lockFileEntry{}
|
||||
for provider, version := range selected {
|
||||
cached := i.targetDir.ProviderVersion(provider, version)
|
||||
if cached == nil {
|
||||
err := fmt.Errorf("selected package for %s is no longer present in the target directory; this is a bug in Terraform", provider)
|
||||
errs[provider] = err
|
||||
if cb := evts.HashPackageFailure; cb != nil {
|
||||
cb(provider, version, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
hash, err := cached.Hash()
|
||||
if err != nil {
|
||||
errs[provider] = fmt.Errorf("failed to calculate checksum for installed provider %s package: %s", provider, err)
|
||||
if cb := evts.HashPackageFailure; cb != nil {
|
||||
cb(provider, version, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
lockEntries[provider] = lockFileEntry{
|
||||
SelectedVersion: version,
|
||||
PackageHash: hash,
|
||||
}
|
||||
}
|
||||
err := i.lockFile().Write(lockEntries)
|
||||
if err != nil {
|
||||
// This is one of few cases where this function does _not_ return an
|
||||
// InstallerError, because failure to write the lock file is a more
|
||||
// general problem, not specific to a certain provider.
|
||||
return selected, fmt.Errorf("failed to record a manifest of selected providers: %s", err)
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return selected, InstallerError{
|
||||
ProviderErrors: errs,
|
||||
|
@ -300,6 +397,60 @@ NeedProvider:
|
|||
return selected, nil
|
||||
}
|
||||
|
||||
func (i *Installer) lockFile() *lockFile {
|
||||
return &lockFile{
|
||||
filename: filepath.Join(i.targetDir.baseDir, "selections.json"),
|
||||
}
|
||||
}
|
||||
|
||||
// SelectedPackages returns the metadata about the packages chosen by the
|
||||
// most recent call to EnsureProviderVersions, which are recorded in a lock
|
||||
// file in the installer's target directory.
|
||||
//
|
||||
// If EnsureProviderVersions has never been run against the current target
|
||||
// directory, the result is a successful empty response indicating that nothing
|
||||
// is selected.
|
||||
//
|
||||
// SelectedPackages also verifies that the package contents are consistent
|
||||
// with the checksums that were recorded at installation time, reporting an
|
||||
// error if not.
|
||||
func (i *Installer) SelectedPackages() (map[addrs.Provider]*CachedProvider, error) {
|
||||
entries, err := i.lockFile().Read()
|
||||
if err != nil {
|
||||
// Read does not return an error for "file not found", so this should
|
||||
// always be some other error.
|
||||
return nil, fmt.Errorf("failed to read selections file: %s", err)
|
||||
}
|
||||
|
||||
ret := make(map[addrs.Provider]*CachedProvider, len(entries))
|
||||
errs := make(map[addrs.Provider]error)
|
||||
for provider, entry := range entries {
|
||||
cached := i.targetDir.ProviderVersion(provider, entry.SelectedVersion)
|
||||
if cached == nil {
|
||||
errs[provider] = fmt.Errorf("package for selected version %s is no longer available in the local cache directory", entry.SelectedVersion)
|
||||
continue
|
||||
}
|
||||
|
||||
ok, err := cached.MatchesHash(entry.PackageHash)
|
||||
if err != nil {
|
||||
errs[provider] = fmt.Errorf("failed to verify checksum for v%s package: %s", entry.SelectedVersion, err)
|
||||
continue
|
||||
}
|
||||
if !ok {
|
||||
errs[provider] = fmt.Errorf("checksum mismatch for v%s package", entry.SelectedVersion)
|
||||
continue
|
||||
}
|
||||
ret[provider] = cached
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return ret, InstallerError{
|
||||
ProviderErrors: errs,
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// InstallMode customizes the details of how an install operation treats
|
||||
// providers that have versions already cached in the target directory.
|
||||
type InstallMode rune
|
||||
|
|
|
@ -40,6 +40,19 @@ type InstallerEvents struct {
|
|||
// available version.
|
||||
ProviderAlreadyInstalled func(provider addrs.Provider, selectedVersion getproviders.Version)
|
||||
|
||||
// The BuiltInProvider... family of events describe the outcome for any
|
||||
// requested providers that are built in to Terraform. Only one of these
|
||||
// methods will be called for each such provider, and no other method
|
||||
// will be called for them except that they are included in the
|
||||
// aggregate PendingProviders map.
|
||||
//
|
||||
// The "Available" event reports that the requested builtin provider is
|
||||
// available in this release of Terraform. The "Failure" event reports
|
||||
// either that the provider is unavailable or that the request for it
|
||||
// is invalid somehow.
|
||||
BuiltInProviderAvailable func(provider addrs.Provider)
|
||||
BuiltInProviderFailure func(provider addrs.Provider, err error)
|
||||
|
||||
// The QueryPackages... family of events delimit the operation of querying
|
||||
// a provider source for information about available packages matching
|
||||
// a particular version constraint, prior to selecting a single version
|
||||
|
@ -93,6 +106,12 @@ type InstallerEvents struct {
|
|||
FetchPackageSuccess func(provider addrs.Provider, version getproviders.Version, localDir string)
|
||||
FetchPackageRetry func(provider addrs.Provider, version getproviders.Version, err error)
|
||||
FetchPackageFailure func(provider addrs.Provider, version getproviders.Version, err error)
|
||||
|
||||
// HashPackageFailure is called if the installer is unable to determine
|
||||
// the hash of the contents of an installed package after installation.
|
||||
// In that case, the selection will not be recorded in the target cache
|
||||
// directory's lock file.
|
||||
HashPackageFailure func(provider addrs.Provider, version getproviders.Version, err error)
|
||||
}
|
||||
|
||||
// OnContext produces a context with all of the same behaviors as the given
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
package providercache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
)
|
||||
|
||||
// lockFile represents a file on disk that captures selected versions and
|
||||
// their associated package checksums resulting from an install process, so
|
||||
// that later consumers of that install process can be sure they are reading
|
||||
// an identical set of providers to what the install process intended.
|
||||
//
|
||||
// This is an internal type used to encapsulate the reading, parsing,
|
||||
// serializing, and writing of lock files. Its public interface is via methods
|
||||
// on type Installer.
|
||||
type lockFile struct {
|
||||
filename string
|
||||
}
|
||||
|
||||
// LockFileEntry represents an entry for a specific provider in a LockFile.
|
||||
type lockFileEntry struct {
|
||||
SelectedVersion getproviders.Version
|
||||
PackageHash string
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*lockFileEntry)(nil)
|
||||
var _ json.Unmarshaler = (*lockFileEntry)(nil)
|
||||
|
||||
// Read returns the current locks captured in the lock file.
|
||||
//
|
||||
// If the file does not exist, the result is successful but empty to indicate
|
||||
// that no providers at all are available for use.
|
||||
func (lf *lockFile) Read() (map[addrs.Provider]lockFileEntry, error) {
|
||||
buf, err := ioutil.ReadFile(lf.filename)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil // no file means no locks yet
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rawEntries map[string]*lockFileEntry
|
||||
err = json.Unmarshal(buf, &rawEntries)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing %s: %s", lf.filename, err)
|
||||
}
|
||||
|
||||
ret := make(map[addrs.Provider]lockFileEntry, len(rawEntries))
|
||||
for providerStr, entry := range rawEntries {
|
||||
provider, diags := addrs.ParseProviderSourceString(providerStr)
|
||||
if diags.HasErrors() {
|
||||
// This file is both generated and consumed by Terraform, so we
|
||||
// don't use super-detailed error messages for problems in it.
|
||||
// If we get here without someone tampering with the file then
|
||||
// it's presumably a bug in either our serializer or our parser.
|
||||
return nil, fmt.Errorf("error parsing %s: invalid provider address %q", lf.filename, providerStr)
|
||||
}
|
||||
ret[provider] = *entry
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Write stores a new set of entries in the lock file, disarding any
|
||||
// selections previously stored there.
|
||||
func (lf *lockFile) Write(new map[addrs.Provider]lockFileEntry) error {
|
||||
toStore := make(map[string]*lockFileEntry, len(new))
|
||||
for provider := range new {
|
||||
entry := new[provider] // so that each reference below is to a different object
|
||||
toStore[provider.String()] = &entry
|
||||
}
|
||||
|
||||
buf, err := json.MarshalIndent(toStore, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing %s: %s", lf.filename, err)
|
||||
}
|
||||
|
||||
os.MkdirAll(
|
||||
filepath.Dir(lf.filename), 0775,
|
||||
) // ignore error since WriteFile below will generate a better one anyway
|
||||
return ioutil.WriteFile(lf.filename, buf, 0664)
|
||||
}
|
||||
|
||||
func (lfe *lockFileEntry) UnmarshalJSON(src []byte) error {
|
||||
type Raw struct {
|
||||
VersionStr string `json:"version"`
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
var raw Raw
|
||||
err := json.Unmarshal(src, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
version, err := getproviders.ParseVersion(raw.VersionStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid version number: %s", err)
|
||||
}
|
||||
lfe.SelectedVersion = version
|
||||
lfe.PackageHash = raw.Hash
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lfe *lockFileEntry) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"version": lfe.SelectedVersion.String(),
|
||||
"hash": lfe.PackageHash,
|
||||
})
|
||||
}
|
|
@ -5,10 +5,13 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
getter "github.com/hashicorp/go-getter"
|
||||
|
||||
"github.com/hashicorp/terraform/httpclient"
|
||||
"github.com/hashicorp/terraform/internal/copydir"
|
||||
)
|
||||
|
||||
// We borrow the "unpack a zip file into a target directory" logic from
|
||||
|
@ -68,6 +71,68 @@ func installFromLocalArchive(ctx context.Context, filename string, targetDir str
|
|||
return unzip.Decompress(targetDir, filename, true)
|
||||
}
|
||||
|
||||
// installFromLocalDir is the implementation of both installing a package from
|
||||
// a local directory source _and_ of linking a package from another cache
|
||||
// in LinkFromOtherCache, because they both do fundamentally the same
|
||||
// operation: symlink if possible, or deep-copy otherwise.
|
||||
func installFromLocalDir(ctx context.Context, sourceDir string, targetDir string) error {
|
||||
return fmt.Errorf("installFromLocalDir not yet implemented")
|
||||
absNew, err := filepath.Abs(targetDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make target path %s absolute: %s", targetDir, err)
|
||||
}
|
||||
absCurrent, err := filepath.Abs(sourceDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make source path %s absolute: %s", sourceDir, err)
|
||||
}
|
||||
|
||||
// Before we do anything else, we'll do a quick check to make sure that
|
||||
// these two paths are not pointing at the same physical directory on
|
||||
// disk. This compares the files by their OS-level device and directory
|
||||
// entry identifiers, not by their virtual filesystem paths.
|
||||
if same, err := copydir.SameFile(absNew, absCurrent); same {
|
||||
return fmt.Errorf("cannot install existing provider directory %s to itself", targetDir)
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to determine if %s and %s are the same: %s", sourceDir, targetDir, err)
|
||||
}
|
||||
|
||||
// Delete anything that's already present at this path first.
|
||||
err = os.RemoveAll(targetDir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove existing %s before linking it to %s: %s", sourceDir, targetDir, err)
|
||||
}
|
||||
|
||||
// We'll prefer to create a symlink if possible, but we'll fall back to
|
||||
// a recursive copy if symlink creation fails. It could fail for a number
|
||||
// of reasons, including being on Windows 8 without administrator
|
||||
// privileges or being on a legacy filesystem like FAT that has no way
|
||||
// to represent a symlink. (Generalized symlink support for Windows was
|
||||
// introduced in a Windows 10 minor update.)
|
||||
//
|
||||
// We use an absolute path for the symlink to reduce the risk of it being
|
||||
// broken by moving things around later, since the source directory is
|
||||
// likely to be a shared directory independent on any particular target
|
||||
// and thus we can't assume that they will move around together.
|
||||
linkTarget := absCurrent
|
||||
|
||||
parentDir := filepath.Dir(absNew)
|
||||
err = os.MkdirAll(parentDir, 0755)
|
||||
if err != nil && os.IsExist(err) {
|
||||
return fmt.Errorf("failed to create parent directories leading to %s: %s", targetDir, err)
|
||||
}
|
||||
|
||||
err = os.Symlink(linkTarget, absNew)
|
||||
if err == nil {
|
||||
// Success, then!
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we get down here then symlinking failed and we need a deep copy
|
||||
// instead.
|
||||
err = copydir.CopyDir(absNew, absCurrent)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to either symlink or copy %s to %s: %s", absCurrent, absNew, err)
|
||||
}
|
||||
|
||||
// If we got here then apparently our copy succeeded, so we're done.
|
||||
return nil
|
||||
}
|
||||
|
|
3
main.go
3
main.go
|
@ -17,7 +17,6 @@ import (
|
|||
"github.com/hashicorp/terraform/command/format"
|
||||
"github.com/hashicorp/terraform/helper/logging"
|
||||
"github.com/hashicorp/terraform/httpclient"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
"github.com/hashicorp/terraform/version"
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-shellwords"
|
||||
|
@ -169,7 +168,7 @@ func wrappedMain() int {
|
|||
// direct from a registry. In future there should be a mechanism to
|
||||
// configure providers sources from the CLI config, which will then
|
||||
// change how we construct this object.
|
||||
providerSrc := getproviders.NewRegistrySource(services)
|
||||
providerSrc := providerSource(services)
|
||||
|
||||
// Initialize the backends.
|
||||
backendInit.Init(services)
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/apparentlymart/go-userdirs/userdirs"
|
||||
|
||||
"github.com/hashicorp/terraform-svchost/disco"
|
||||
"github.com/hashicorp/terraform/command/cliconfig"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
)
|
||||
|
||||
// providerSource constructs a provider source based on a combination of the
|
||||
// CLI configuration and some default search locations. This will be the
|
||||
// provider source used for provider installation in the "terraform init"
|
||||
// command, unless overridden by the special -plugin-dir option.
|
||||
func providerSource(services *disco.Disco) getproviders.Source {
|
||||
// We're not yet using the CLI config here because we've not implemented
|
||||
// yet the new configuration constructs to customize provider search
|
||||
// locations. That'll come later.
|
||||
// For now, we have a fixed set of search directories:
|
||||
// - The "terraform.d/plugins" directory in the current working directory,
|
||||
// which we've historically documented as a place to put plugins as a
|
||||
// way to include them in bundles uploaded to Terraform Cloud, where
|
||||
// there has historically otherwise been no way to use custom providers.
|
||||
// - The "plugins" subdirectory of the CLI config search directory.
|
||||
// (thats ~/.terraform.d/plugins on Unix systems, equivalents elsewhere)
|
||||
// - The "plugins" subdirectory of any platform-specific search paths,
|
||||
// following e.g. the XDG base directory specification on Unix systems,
|
||||
// Apple's guidelines on OS X, and "known folders" on Windows.
|
||||
//
|
||||
// Those directories are checked in addition to the direct upstream
|
||||
// registry specified in the provider's address.
|
||||
var searchRules []getproviders.MultiSourceSelector
|
||||
|
||||
addLocalDir := func(dir string) {
|
||||
// We'll make sure the directory actually exists before we add it,
|
||||
// because otherwise installation would always fail trying to look
|
||||
// in non-existent directories. (This is done here rather than in
|
||||
// the source itself because explicitly-selected directories via the
|
||||
// CLI config, once we have them, _should_ produce an error if they
|
||||
// don't exist to help users get their configurations right.)
|
||||
if info, err := os.Stat(dir); err == nil && info.IsDir() {
|
||||
log.Printf("[DEBUG] will search for provider plugins in %s", dir)
|
||||
searchRules = append(searchRules, getproviders.MultiSourceSelector{
|
||||
Source: getproviders.NewFilesystemMirrorSource(dir),
|
||||
})
|
||||
} else {
|
||||
log.Printf("[DEBUG] ignoring non-existing provider search directory %s", dir)
|
||||
}
|
||||
}
|
||||
|
||||
addLocalDir("terraform.d/plugins") // our "vendor" directory
|
||||
cliConfigDir, err := cliconfig.ConfigDir()
|
||||
if err != nil {
|
||||
addLocalDir(filepath.Join(cliConfigDir, "plugins"))
|
||||
}
|
||||
|
||||
// This "userdirs" library implements an appropriate user-specific and
|
||||
// app-specific directory layout for the current platform, such as XDG Base
|
||||
// Directory on Unix, using the following name strings to construct a
|
||||
// suitable application-specific subdirectory name following the
|
||||
// conventions for each platform:
|
||||
//
|
||||
// XDG (Unix): lowercase of the first string, "terraform"
|
||||
// Windows: two-level heirarchy of first two strings, "HashiCorp\Terraform"
|
||||
// OS X: reverse-DNS unique identifier, "io.terraform".
|
||||
sysSpecificDirs := userdirs.ForApp("Terraform", "HashiCorp", "io.terraform")
|
||||
for _, dir := range sysSpecificDirs.DataSearchPaths("plugins") {
|
||||
addLocalDir(dir)
|
||||
}
|
||||
|
||||
// Last but not least, the main registry source! We'll wrap a caching
|
||||
// layer around this one to help optimize the several network requests
|
||||
// we'll end up making to it while treating it as one of several sources
|
||||
// in a MultiSource (as recommended in the MultiSource docs).
|
||||
// This one is listed last so that if a particular version is available
|
||||
// both in one of the above directories _and_ in a remote registry, the
|
||||
// local copy will take precedence.
|
||||
searchRules = append(searchRules, getproviders.MultiSourceSelector{
|
||||
Source: getproviders.NewMemoizeSource(
|
||||
getproviders.NewRegistrySource(services),
|
||||
),
|
||||
})
|
||||
|
||||
return getproviders.MultiSource(searchRules)
|
||||
}
|
|
@ -1,56 +1,5 @@
|
|||
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
|
||||
// provider, or returns an error if that is impossible.
|
||||
type Factory func() (Interface, error)
|
|
@ -46,7 +46,7 @@ func TestSession_basicState(t *testing.T) {
|
|||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -61,7 +61,7 @@ func TestSession_basicState(t *testing.T) {
|
|||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("test"),
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -213,9 +213,9 @@ func testSession(t *testing.T, test testSessionTest) {
|
|||
// Build the TF context
|
||||
ctx, diags := terraform.NewContext(&terraform.ContextOpts{
|
||||
State: test.State,
|
||||
ProviderResolver: providers.ResolverFixed(map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("test"): providers.FactoryFixed(p),
|
||||
}),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): providers.FactoryFixed(p),
|
||||
},
|
||||
Config: config,
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"type": "null_resource",
|
||||
"name": "bar",
|
||||
"each": "list",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"instances": [
|
||||
{
|
||||
"attributes_flat": {
|
||||
|
@ -60,7 +60,7 @@
|
|||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "baz",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"instances": [
|
||||
{
|
||||
"attributes_flat": {
|
||||
|
@ -86,7 +86,7 @@
|
|||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "foo",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"instances": [
|
||||
{
|
||||
"attributes_flat": {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"type": "null_resource",
|
||||
"name": "bar",
|
||||
"each": "list",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"instances": [
|
||||
{
|
||||
"attributes_flat": {
|
||||
|
@ -60,7 +60,7 @@
|
|||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "baz",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"instances": [
|
||||
{
|
||||
"attributes_flat": {
|
||||
|
@ -86,7 +86,7 @@
|
|||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "foo",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"instances": [
|
||||
{
|
||||
"attributes_flat": {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "bar",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "bar",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
|
@ -35,7 +35,7 @@
|
|||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "foo",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"each": "list",
|
||||
"instances": [
|
||||
{
|
||||
|
@ -62,7 +62,7 @@
|
|||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "foobar",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"provider": "provider.null",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "foo",
|
||||
"provider": "provider.null",
|
||||
"provider": "provider[\"registry.terraform.io/-/null\"]",
|
||||
"each": "list",
|
||||
"instances": [
|
||||
{
|
||||
|
|
|
@ -136,13 +136,19 @@ func upgradeStateV3ToV4(old *stateV3) (*stateV4, error) {
|
|||
return nil, fmt.Errorf("invalid legacy provider config reference %q for %s: %s", oldProviderAddr, instAddr, diags.Err())
|
||||
}
|
||||
providerAddr = addrs.AbsProviderConfig{
|
||||
Module: moduleAddr.Module(),
|
||||
Module: moduleAddr.Module(),
|
||||
// We use NewLegacyProvider here so we can use
|
||||
// LegacyString() below to get the appropriate
|
||||
// legacy-style provider string.
|
||||
Provider: addrs.NewLegacyProvider(localAddr.LocalName),
|
||||
Alias: localAddr.Alias,
|
||||
}
|
||||
} else {
|
||||
providerAddr = addrs.AbsProviderConfig{
|
||||
Module: moduleAddr.Module(),
|
||||
Module: moduleAddr.Module(),
|
||||
// We use NewLegacyProvider here so we can use
|
||||
// LegacyString() below to get the appropriate
|
||||
// legacy-style provider string.
|
||||
Provider: addrs.NewLegacyProvider(resAddr.ImpliedProvider()),
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +160,7 @@ func upgradeStateV3ToV4(old *stateV3) (*stateV4, error) {
|
|||
Type: resAddr.Type,
|
||||
Name: resAddr.Name,
|
||||
Instances: []instanceObjectStateV4{},
|
||||
ProviderConfig: providerAddr.String(),
|
||||
ProviderConfig: providerAddr.LegacyString(),
|
||||
}
|
||||
resourceStates[resAddr.String()] = rs
|
||||
}
|
||||
|
|
|
@ -55,10 +55,10 @@ type ContextOpts struct {
|
|||
Meta *ContextMeta
|
||||
Destroy bool
|
||||
|
||||
Hooks []Hook
|
||||
Parallelism int
|
||||
ProviderResolver providers.Resolver
|
||||
Provisioners map[string]ProvisionerFactory
|
||||
Hooks []Hook
|
||||
Parallelism int
|
||||
Providers map[addrs.Provider]providers.Factory
|
||||
Provisioners map[string]provisioners.Factory
|
||||
|
||||
// If non-nil, will apply as additional constraints on the provider
|
||||
// plugins that will be requested from the provider resolver.
|
||||
|
@ -169,28 +169,8 @@ func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) {
|
|||
// override the defaults.
|
||||
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{
|
||||
providers: providerFactories,
|
||||
providers: opts.Providers,
|
||||
provisioners: opts.Provisioners,
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -32,7 +32,7 @@ type basicComponentFactory struct {
|
|||
func (c *basicComponentFactory) ResourceProviders() []string {
|
||||
var result []string
|
||||
for k := range c.providers {
|
||||
result = append(result, k.LegacyString())
|
||||
result = append(result, k.String())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func (c *basicComponentFactory) ResourceProvisioners() []string {
|
|||
func (c *basicComponentFactory) ResourceProvider(typ addrs.Provider) (providers.Interface, error) {
|
||||
f, ok := c.providers[typ]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown provider %q", typ.LegacyString())
|
||||
return nil, fmt.Errorf("unknown provider %q", typ.String())
|
||||
}
|
||||
|
||||
return f()
|
||||
|
|
|
@ -28,7 +28,7 @@ func simpleMockComponentFactory() *basicComponentFactory {
|
|||
provisioner := simpleMockProvisioner()
|
||||
return &basicComponentFactory{
|
||||
providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("test"): func() (providers.Interface, error) {
|
||||
addrs.NewDefaultProvider("test"): func() (providers.Interface, error) {
|
||||
return provider, nil
|
||||
},
|
||||
},
|
||||
|
|
|
@ -14,9 +14,9 @@ import (
|
|||
// to create a base testing scenario. This is used to represent some common
|
||||
// situations used as the basis for multiple tests.
|
||||
type contextTestFixture struct {
|
||||
Config *configs.Config
|
||||
ProviderResolver providers.Resolver
|
||||
Provisioners map[string]ProvisionerFactory
|
||||
Config *configs.Config
|
||||
Providers map[addrs.Provider]providers.Factory
|
||||
Provisioners map[string]ProvisionerFactory
|
||||
}
|
||||
|
||||
// ContextOpts returns a ContextOps pre-populated with the elements of this
|
||||
|
@ -24,9 +24,9 @@ type contextTestFixture struct {
|
|||
// _shallow_ modifications to the options as needed.
|
||||
func (f *contextTestFixture) ContextOpts() *ContextOpts {
|
||||
return &ContextOpts{
|
||||
Config: f.Config,
|
||||
ProviderResolver: f.ProviderResolver,
|
||||
Provisioners: f.Provisioners,
|
||||
Config: f.Config,
|
||||
Providers: f.Providers,
|
||||
Provisioners: f.Provisioners,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,11 +52,9 @@ func contextFixtureApplyVars(t *testing.T) *contextTestFixture {
|
|||
p.DiffFn = testDiffFn
|
||||
return &contextTestFixture{
|
||||
Config: c,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,10 +78,8 @@ func contextFixtureApplyVarsEnv(t *testing.T) *contextTestFixture {
|
|||
p.DiffFn = testDiffFn
|
||||
return &contextTestFixture{
|
||||
Config: c,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,9 @@ func TestContextImport_basic(t *testing.T) {
|
|||
m := testModule(t, "import-provider")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -57,11 +55,9 @@ func TestContextImport_basic_errpr(t *testing.T) {
|
|||
m := testModule(t, "import-provider")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -92,11 +88,9 @@ func TestContextImport_countIndex(t *testing.T) {
|
|||
m := testModule(t, "import-provider")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -132,11 +126,9 @@ func TestContextImport_collision(t *testing.T) {
|
|||
m := testModule(t, "import-provider")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
|
||||
State: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
|
@ -152,7 +144,7 @@ func TestContextImport_collision(t *testing.T) {
|
|||
Status: states.ObjectReady,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewLegacyProvider("aws"),
|
||||
Provider: addrs.NewDefaultProvider("aws"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
@ -183,7 +175,7 @@ func TestContextImport_collision(t *testing.T) {
|
|||
actual := strings.TrimSpace(state.String())
|
||||
expected := `aws_instance.foo:
|
||||
ID = bar
|
||||
provider = provider["registry.terraform.io/-/aws"]`
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]`
|
||||
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: \n%s", actual)
|
||||
|
@ -202,11 +194,9 @@ func TestContextImport_missingType(t *testing.T) {
|
|||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
state, diags := ctx.Import(&ImportOpts{
|
||||
|
@ -254,11 +244,9 @@ func TestContextImport_moduleProvider(t *testing.T) {
|
|||
m := testModule(t, "import-provider")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
state, diags := ctx.Import(&ImportOpts{
|
||||
|
@ -292,11 +280,9 @@ func TestContextImport_providerModule(t *testing.T) {
|
|||
m := testModule(t, "import-module")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -343,11 +329,9 @@ func TestContextImport_providerVarConfig(t *testing.T) {
|
|||
m := testModule(t, "import-provider-vars")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
Variables: InputValues{
|
||||
"foo": &InputValue{
|
||||
Value: cty.StringVal("bar"),
|
||||
|
@ -405,11 +389,9 @@ func TestContextImport_providerNonVarConfig(t *testing.T) {
|
|||
m := testModule(t, "import-provider-non-vars")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -439,11 +421,9 @@ func TestContextImport_refresh(t *testing.T) {
|
|||
m := testModule(t, "import-provider")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -488,11 +468,9 @@ func TestContextImport_refreshNil(t *testing.T) {
|
|||
m := testModule(t, "import-provider")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -534,11 +512,9 @@ func TestContextImport_module(t *testing.T) {
|
|||
m := testModule(t, "import-module")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -574,11 +550,9 @@ func TestContextImport_moduleDepth2(t *testing.T) {
|
|||
m := testModule(t, "import-module")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -614,11 +588,9 @@ func TestContextImport_moduleDiff(t *testing.T) {
|
|||
m := testModule(t, "import-module")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportStateReturn = []*InstanceState{
|
||||
|
@ -686,11 +658,9 @@ func TestContextImport_multiState(t *testing.T) {
|
|||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
state, diags := ctx.Import(&ImportOpts{
|
||||
|
@ -755,11 +725,9 @@ func TestContextImport_multiStateSame(t *testing.T) {
|
|||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
ProviderResolver: providers.ResolverFixed(
|
||||
map[addrs.Provider]providers.Factory{
|
||||
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
),
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
state, diags := ctx.Import(&ImportOpts{
|
||||
|
@ -786,13 +754,13 @@ func TestContextImport_multiStateSame(t *testing.T) {
|
|||
const testImportStr = `
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
`
|
||||
|
||||
const testImportCountIndexStr = `
|
||||
aws_instance.foo.0:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
`
|
||||
|
||||
const testImportModuleStr = `
|
||||
|
@ -800,7 +768,7 @@ const testImportModuleStr = `
|
|||
module.child:
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
`
|
||||
|
||||
const testImportModuleDepth2Str = `
|
||||
|
@ -808,7 +776,7 @@ const testImportModuleDepth2Str = `
|
|||
module.child.nested:
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
`
|
||||
|
||||
const testImportModuleExistingStr = `
|
||||
|
@ -816,36 +784,36 @@ const testImportModuleExistingStr = `
|
|||
module.foo:
|
||||
aws_instance.bar:
|
||||
ID = bar
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
`
|
||||
|
||||
const testImportMultiStr = `
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
aws_instance_thing.foo:
|
||||
ID = bar
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
`
|
||||
|
||||
const testImportMultiSameStr = `
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
aws_instance_thing.foo:
|
||||
ID = bar
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
aws_instance_thing.foo-1:
|
||||
ID = qux
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
`
|
||||
|
||||
const testImportRefreshStr = `
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
foo = bar
|
||||
`
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue