mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-23 23:50:12 -06:00
425 lines
16 KiB
Go
425 lines
16 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package configs
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/go-test/deep"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
version "github.com/hashicorp/go-version"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
svchost "github.com/hashicorp/terraform-svchost"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/depsfile"
|
|
"github.com/hashicorp/terraform/internal/getproviders"
|
|
)
|
|
|
|
func TestConfigProviderTypes(t *testing.T) {
|
|
// nil cfg should return an empty map
|
|
got := NewEmptyConfig().ProviderTypes()
|
|
if len(got) != 0 {
|
|
t.Fatal("expected empty result from empty config")
|
|
}
|
|
|
|
cfg, diags := testModuleConfigFromFile("testdata/valid-files/providers-explicit-implied.tf")
|
|
if diags.HasErrors() {
|
|
t.Fatal(diags.Error())
|
|
}
|
|
|
|
got = cfg.ProviderTypes()
|
|
want := []addrs.Provider{
|
|
addrs.NewDefaultProvider("aws"),
|
|
addrs.NewDefaultProvider("null"),
|
|
addrs.NewDefaultProvider("template"),
|
|
addrs.NewDefaultProvider("test"),
|
|
}
|
|
for _, problem := range deep.Equal(got, want) {
|
|
t.Error(problem)
|
|
}
|
|
}
|
|
|
|
func TestConfigProviderTypes_nested(t *testing.T) {
|
|
// basic test with a nil config
|
|
c := NewEmptyConfig()
|
|
got := c.ProviderTypes()
|
|
if len(got) != 0 {
|
|
t.Fatalf("wrong result!\ngot: %#v\nwant: nil\n", got)
|
|
}
|
|
|
|
// config with two provider sources, and one implicit (default) provider
|
|
cfg, diags := testNestedModuleConfigFromDir(t, "testdata/valid-modules/nested-providers-fqns")
|
|
if diags.HasErrors() {
|
|
t.Fatal(diags.Error())
|
|
}
|
|
|
|
got = cfg.ProviderTypes()
|
|
want := []addrs.Provider{
|
|
addrs.NewProvider(addrs.DefaultProviderRegistryHost, "bar", "test"),
|
|
addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test"),
|
|
addrs.NewDefaultProvider("test"),
|
|
}
|
|
|
|
for _, problem := range deep.Equal(got, want) {
|
|
t.Error(problem)
|
|
}
|
|
}
|
|
|
|
func TestConfigResolveAbsProviderAddr(t *testing.T) {
|
|
cfg, diags := testModuleConfigFromDir("testdata/providers-explicit-fqn")
|
|
if diags.HasErrors() {
|
|
t.Fatal(diags.Error())
|
|
}
|
|
|
|
t.Run("already absolute", func(t *testing.T) {
|
|
addr := addrs.AbsProviderConfig{
|
|
Module: addrs.RootModule,
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Alias: "boop",
|
|
}
|
|
got := cfg.ResolveAbsProviderAddr(addr, addrs.RootModule)
|
|
if got, want := got.String(), addr.String(); got != want {
|
|
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
})
|
|
t.Run("local, implied mapping", func(t *testing.T) {
|
|
addr := addrs.LocalProviderConfig{
|
|
LocalName: "implied",
|
|
Alias: "boop",
|
|
}
|
|
got := cfg.ResolveAbsProviderAddr(addr, addrs.RootModule)
|
|
want := addrs.AbsProviderConfig{
|
|
Module: addrs.RootModule,
|
|
Provider: addrs.NewDefaultProvider("implied"),
|
|
Alias: "boop",
|
|
}
|
|
if got, want := got.String(), want.String(); got != want {
|
|
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
})
|
|
t.Run("local, explicit mapping", func(t *testing.T) {
|
|
addr := addrs.LocalProviderConfig{
|
|
LocalName: "foo-test", // this is explicitly set in the config
|
|
Alias: "boop",
|
|
}
|
|
got := cfg.ResolveAbsProviderAddr(addr, addrs.RootModule)
|
|
want := addrs.AbsProviderConfig{
|
|
Module: addrs.RootModule,
|
|
Provider: addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test"),
|
|
Alias: "boop",
|
|
}
|
|
if got, want := got.String(), want.String(); got != want {
|
|
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestConfigProviderRequirements(t *testing.T) {
|
|
cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs")
|
|
// TODO: Version Constraint Deprecation.
|
|
// Once we've removed the version argument from provider configuration
|
|
// blocks, this can go back to expected 0 diagnostics.
|
|
// assertNoDiagnostics(t, diags)
|
|
assertDiagnosticCount(t, diags, 1)
|
|
assertDiagnosticSummary(t, diags, "Version constraints inside provider configuration blocks are deprecated")
|
|
|
|
tlsProvider := addrs.NewProvider(
|
|
addrs.DefaultProviderRegistryHost,
|
|
"hashicorp", "tls",
|
|
)
|
|
happycloudProvider := addrs.NewProvider(
|
|
svchost.Hostname("tf.example.com"),
|
|
"awesomecorp", "happycloud",
|
|
)
|
|
nullProvider := addrs.NewDefaultProvider("null")
|
|
randomProvider := addrs.NewDefaultProvider("random")
|
|
impliedProvider := addrs.NewDefaultProvider("implied")
|
|
terraformProvider := addrs.NewBuiltInProvider("terraform")
|
|
configuredProvider := addrs.NewDefaultProvider("configured")
|
|
grandchildProvider := addrs.NewDefaultProvider("grandchild")
|
|
|
|
got, diags := cfg.ProviderRequirements()
|
|
assertNoDiagnostics(t, diags)
|
|
want := getproviders.Requirements{
|
|
// the nullProvider constraints from the two modules are merged
|
|
nullProvider: getproviders.MustParseVersionConstraints("~> 2.0.0, 2.0.1"),
|
|
randomProvider: getproviders.MustParseVersionConstraints("~> 1.2.0"),
|
|
tlsProvider: getproviders.MustParseVersionConstraints("~> 3.0"),
|
|
configuredProvider: getproviders.MustParseVersionConstraints("~> 1.4"),
|
|
impliedProvider: nil,
|
|
happycloudProvider: nil,
|
|
terraformProvider: nil,
|
|
grandchildProvider: nil,
|
|
}
|
|
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
}
|
|
|
|
func TestConfigProviderRequirementsDuplicate(t *testing.T) {
|
|
_, diags := testNestedModuleConfigFromDir(t, "testdata/duplicate-local-name")
|
|
assertDiagnosticCount(t, diags, 3)
|
|
assertDiagnosticSummary(t, diags, "Duplicate required provider")
|
|
}
|
|
|
|
func TestConfigProviderRequirementsShallow(t *testing.T) {
|
|
cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs")
|
|
// TODO: Version Constraint Deprecation.
|
|
// Once we've removed the version argument from provider configuration
|
|
// blocks, this can go back to expected 0 diagnostics.
|
|
// assertNoDiagnostics(t, diags)
|
|
assertDiagnosticCount(t, diags, 1)
|
|
assertDiagnosticSummary(t, diags, "Version constraints inside provider configuration blocks are deprecated")
|
|
|
|
tlsProvider := addrs.NewProvider(
|
|
addrs.DefaultProviderRegistryHost,
|
|
"hashicorp", "tls",
|
|
)
|
|
nullProvider := addrs.NewDefaultProvider("null")
|
|
randomProvider := addrs.NewDefaultProvider("random")
|
|
impliedProvider := addrs.NewDefaultProvider("implied")
|
|
terraformProvider := addrs.NewBuiltInProvider("terraform")
|
|
configuredProvider := addrs.NewDefaultProvider("configured")
|
|
|
|
got, diags := cfg.ProviderRequirementsShallow()
|
|
assertNoDiagnostics(t, diags)
|
|
want := getproviders.Requirements{
|
|
// the nullProvider constraint is only from the root module
|
|
nullProvider: getproviders.MustParseVersionConstraints("~> 2.0.0"),
|
|
randomProvider: getproviders.MustParseVersionConstraints("~> 1.2.0"),
|
|
tlsProvider: getproviders.MustParseVersionConstraints("~> 3.0"),
|
|
configuredProvider: getproviders.MustParseVersionConstraints("~> 1.4"),
|
|
impliedProvider: nil,
|
|
terraformProvider: nil,
|
|
}
|
|
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
}
|
|
|
|
func TestConfigProviderRequirementsByModule(t *testing.T) {
|
|
cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs")
|
|
// TODO: Version Constraint Deprecation.
|
|
// Once we've removed the version argument from provider configuration
|
|
// blocks, this can go back to expected 0 diagnostics.
|
|
// assertNoDiagnostics(t, diags)
|
|
assertDiagnosticCount(t, diags, 1)
|
|
assertDiagnosticSummary(t, diags, "Version constraints inside provider configuration blocks are deprecated")
|
|
|
|
tlsProvider := addrs.NewProvider(
|
|
addrs.DefaultProviderRegistryHost,
|
|
"hashicorp", "tls",
|
|
)
|
|
happycloudProvider := addrs.NewProvider(
|
|
svchost.Hostname("tf.example.com"),
|
|
"awesomecorp", "happycloud",
|
|
)
|
|
nullProvider := addrs.NewDefaultProvider("null")
|
|
randomProvider := addrs.NewDefaultProvider("random")
|
|
impliedProvider := addrs.NewDefaultProvider("implied")
|
|
terraformProvider := addrs.NewBuiltInProvider("terraform")
|
|
configuredProvider := addrs.NewDefaultProvider("configured")
|
|
grandchildProvider := addrs.NewDefaultProvider("grandchild")
|
|
|
|
got, diags := cfg.ProviderRequirementsByModule()
|
|
assertNoDiagnostics(t, diags)
|
|
want := &ModuleRequirements{
|
|
Name: "",
|
|
SourceAddr: nil,
|
|
SourceDir: "testdata/provider-reqs",
|
|
Requirements: getproviders.Requirements{
|
|
// Only the root module's version is present here
|
|
nullProvider: getproviders.MustParseVersionConstraints("~> 2.0.0"),
|
|
randomProvider: getproviders.MustParseVersionConstraints("~> 1.2.0"),
|
|
tlsProvider: getproviders.MustParseVersionConstraints("~> 3.0"),
|
|
configuredProvider: getproviders.MustParseVersionConstraints("~> 1.4"),
|
|
impliedProvider: nil,
|
|
terraformProvider: nil,
|
|
},
|
|
Children: map[string]*ModuleRequirements{
|
|
"kinder": {
|
|
Name: "kinder",
|
|
SourceAddr: addrs.ModuleSourceLocal("./child"),
|
|
SourceDir: "testdata/provider-reqs/child",
|
|
Requirements: getproviders.Requirements{
|
|
nullProvider: getproviders.MustParseVersionConstraints("= 2.0.1"),
|
|
happycloudProvider: nil,
|
|
},
|
|
Children: map[string]*ModuleRequirements{
|
|
"nested": {
|
|
Name: "nested",
|
|
SourceAddr: addrs.ModuleSourceLocal("./grandchild"),
|
|
SourceDir: "testdata/provider-reqs/child/grandchild",
|
|
Requirements: getproviders.Requirements{
|
|
grandchildProvider: nil,
|
|
},
|
|
Children: map[string]*ModuleRequirements{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ignore := cmpopts.IgnoreUnexported(version.Constraint{}, cty.Value{}, hclsyntax.Body{})
|
|
if diff := cmp.Diff(want, got, ignore); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
}
|
|
|
|
func TestVerifyDependencySelections(t *testing.T) {
|
|
cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs")
|
|
// TODO: Version Constraint Deprecation.
|
|
// Once we've removed the version argument from provider configuration
|
|
// blocks, this can go back to expected 0 diagnostics.
|
|
// assertNoDiagnostics(t, diags)
|
|
assertDiagnosticCount(t, diags, 1)
|
|
assertDiagnosticSummary(t, diags, "Version constraints inside provider configuration blocks are deprecated")
|
|
|
|
tlsProvider := addrs.NewProvider(
|
|
addrs.DefaultProviderRegistryHost,
|
|
"hashicorp", "tls",
|
|
)
|
|
happycloudProvider := addrs.NewProvider(
|
|
svchost.Hostname("tf.example.com"),
|
|
"awesomecorp", "happycloud",
|
|
)
|
|
nullProvider := addrs.NewDefaultProvider("null")
|
|
randomProvider := addrs.NewDefaultProvider("random")
|
|
impliedProvider := addrs.NewDefaultProvider("implied")
|
|
configuredProvider := addrs.NewDefaultProvider("configured")
|
|
grandchildProvider := addrs.NewDefaultProvider("grandchild")
|
|
|
|
tests := map[string]struct {
|
|
PrepareLocks func(*depsfile.Locks)
|
|
WantErrs []string
|
|
}{
|
|
"empty locks": {
|
|
func(*depsfile.Locks) {
|
|
// Intentionally blank
|
|
},
|
|
[]string{
|
|
`provider registry.terraform.io/hashicorp/configured: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/grandchild: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/implied: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/null: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/random: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/tls: required by this configuration but no version is selected`,
|
|
`provider tf.example.com/awesomecorp/happycloud: required by this configuration but no version is selected`,
|
|
},
|
|
},
|
|
"suitable locks": {
|
|
func(locks *depsfile.Locks) {
|
|
locks.SetProvider(configuredProvider, getproviders.MustParseVersion("1.4.0"), nil, nil)
|
|
locks.SetProvider(grandchildProvider, getproviders.MustParseVersion("0.1.0"), nil, nil)
|
|
locks.SetProvider(impliedProvider, getproviders.MustParseVersion("0.2.0"), nil, nil)
|
|
locks.SetProvider(nullProvider, getproviders.MustParseVersion("2.0.1"), nil, nil)
|
|
locks.SetProvider(randomProvider, getproviders.MustParseVersion("1.2.2"), nil, nil)
|
|
locks.SetProvider(tlsProvider, getproviders.MustParseVersion("3.0.1"), nil, nil)
|
|
locks.SetProvider(happycloudProvider, getproviders.MustParseVersion("0.0.1"), nil, nil)
|
|
},
|
|
nil,
|
|
},
|
|
"null provider constraints changed": {
|
|
func(locks *depsfile.Locks) {
|
|
locks.SetProvider(configuredProvider, getproviders.MustParseVersion("1.4.0"), nil, nil)
|
|
locks.SetProvider(grandchildProvider, getproviders.MustParseVersion("0.1.0"), nil, nil)
|
|
locks.SetProvider(impliedProvider, getproviders.MustParseVersion("0.2.0"), nil, nil)
|
|
locks.SetProvider(nullProvider, getproviders.MustParseVersion("3.0.0"), nil, nil)
|
|
locks.SetProvider(randomProvider, getproviders.MustParseVersion("1.2.2"), nil, nil)
|
|
locks.SetProvider(tlsProvider, getproviders.MustParseVersion("3.0.1"), nil, nil)
|
|
locks.SetProvider(happycloudProvider, getproviders.MustParseVersion("0.0.1"), nil, nil)
|
|
},
|
|
[]string{
|
|
`provider registry.terraform.io/hashicorp/null: locked version selection 3.0.0 doesn't match the updated version constraints "~> 2.0.0, 2.0.1"`,
|
|
},
|
|
},
|
|
"null provider lock changed": {
|
|
func(locks *depsfile.Locks) {
|
|
// In this case, we set the lock file version constraints to
|
|
// match the configuration, and so our error message changes
|
|
// to not assume the configuration changed anymore.
|
|
locks.SetProvider(nullProvider, getproviders.MustParseVersion("3.0.0"), getproviders.MustParseVersionConstraints("~> 2.0.0, 2.0.1"), nil)
|
|
|
|
locks.SetProvider(configuredProvider, getproviders.MustParseVersion("1.4.0"), nil, nil)
|
|
locks.SetProvider(grandchildProvider, getproviders.MustParseVersion("0.1.0"), nil, nil)
|
|
locks.SetProvider(impliedProvider, getproviders.MustParseVersion("0.2.0"), nil, nil)
|
|
locks.SetProvider(randomProvider, getproviders.MustParseVersion("1.2.2"), nil, nil)
|
|
locks.SetProvider(tlsProvider, getproviders.MustParseVersion("3.0.1"), nil, nil)
|
|
locks.SetProvider(happycloudProvider, getproviders.MustParseVersion("0.0.1"), nil, nil)
|
|
},
|
|
[]string{
|
|
`provider registry.terraform.io/hashicorp/null: version constraints "~> 2.0.0, 2.0.1" don't match the locked version selection 3.0.0`,
|
|
},
|
|
},
|
|
"overridden provider": {
|
|
func(locks *depsfile.Locks) {
|
|
locks.SetProviderOverridden(happycloudProvider)
|
|
},
|
|
[]string{
|
|
// We still catch all of the other ones, because only happycloud was overridden
|
|
`provider registry.terraform.io/hashicorp/configured: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/grandchild: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/implied: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/null: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/random: required by this configuration but no version is selected`,
|
|
`provider registry.terraform.io/hashicorp/tls: required by this configuration but no version is selected`,
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
depLocks := depsfile.NewLocks()
|
|
test.PrepareLocks(depLocks)
|
|
gotErrs := cfg.VerifyDependencySelections(depLocks)
|
|
|
|
var gotErrsStr []string
|
|
if gotErrs != nil {
|
|
gotErrsStr = make([]string, len(gotErrs))
|
|
for i, err := range gotErrs {
|
|
gotErrsStr[i] = err.Error()
|
|
}
|
|
}
|
|
|
|
if diff := cmp.Diff(test.WantErrs, gotErrsStr); diff != "" {
|
|
t.Errorf("wrong errors\n%s", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigProviderForConfigAddr(t *testing.T) {
|
|
cfg, diags := testModuleConfigFromDir("testdata/valid-modules/providers-fqns")
|
|
assertNoDiagnostics(t, diags)
|
|
|
|
got := cfg.ProviderForConfigAddr(addrs.NewDefaultLocalProviderConfig("foo-test"))
|
|
want := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test")
|
|
if !got.Equals(want) {
|
|
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
|
|
// now check a provider that isn't in the configuration. It should return a DefaultProvider.
|
|
got = cfg.ProviderForConfigAddr(addrs.NewDefaultLocalProviderConfig("bar-test"))
|
|
want = addrs.NewDefaultProvider("bar-test")
|
|
if !got.Equals(want) {
|
|
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
}
|
|
|
|
func TestConfigAddProviderRequirements(t *testing.T) {
|
|
cfg, diags := testModuleConfigFromFile("testdata/valid-files/providers-explicit-implied.tf")
|
|
assertNoDiagnostics(t, diags)
|
|
|
|
reqs := getproviders.Requirements{
|
|
addrs.NewDefaultProvider("null"): nil,
|
|
}
|
|
diags = cfg.addProviderRequirements(reqs, true)
|
|
assertNoDiagnostics(t, diags)
|
|
}
|