mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
b9a93a0fe7
This is part of a general effort to move all of Terraform's non-library package surface under internal in order to reinforce that these are for internal use within Terraform only. If you were previously importing packages under this prefix into an external codebase, you could pin to an earlier release tag as an interim solution until you've make a plan to achieve the same functionality some other way.
550 lines
15 KiB
Go
550 lines
15 KiB
Go
package getproviders
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
)
|
|
|
|
func TestMultiSourceAvailableVersions(t *testing.T) {
|
|
platform1 := Platform{OS: "amigaos", Arch: "m68k"}
|
|
platform2 := Platform{OS: "aros", Arch: "arm"}
|
|
|
|
t.Run("unfiltered merging", func(t *testing.T) {
|
|
s1 := NewMockSource([]PackageMeta{
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
),
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform2,
|
|
),
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("bar"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform2,
|
|
),
|
|
},
|
|
nil,
|
|
)
|
|
s2 := NewMockSource([]PackageMeta{
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
),
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.2.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
),
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("bar"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
),
|
|
},
|
|
nil,
|
|
)
|
|
multi := MultiSource{
|
|
{Source: s1},
|
|
{Source: s2},
|
|
}
|
|
|
|
// AvailableVersions produces the union of all versions available
|
|
// across all of the sources.
|
|
got, _, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("foo"))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
want := VersionList{
|
|
MustParseVersion("1.0.0"),
|
|
MustParseVersion("1.2.0"),
|
|
}
|
|
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
|
|
_, _, err = multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("baz"))
|
|
if want, ok := err.(ErrRegistryProviderNotKnown); !ok {
|
|
t.Fatalf("wrong error type:\ngot: %T\nwant: %T", err, want)
|
|
}
|
|
})
|
|
|
|
t.Run("merging with filters", func(t *testing.T) {
|
|
// This is just testing that filters are being honored at all, using a
|
|
// specific pair of filters. The different filter combinations
|
|
// themselves are tested in TestMultiSourceSelector.
|
|
|
|
s1 := NewMockSource([]PackageMeta{
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
),
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("bar"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
),
|
|
},
|
|
nil,
|
|
)
|
|
s2 := NewMockSource([]PackageMeta{
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.2.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
),
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("bar"),
|
|
MustParseVersion("1.2.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
),
|
|
},
|
|
nil,
|
|
)
|
|
multi := MultiSource{
|
|
{
|
|
Source: s1,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
|
|
},
|
|
{
|
|
Source: s2,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/bar"),
|
|
},
|
|
}
|
|
|
|
got, _, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("foo"))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
want := VersionList{
|
|
MustParseVersion("1.0.0"),
|
|
// 1.2.0 isn't present because s3 doesn't include "foo"
|
|
}
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
|
|
got, _, err = multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("bar"))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
want = VersionList{
|
|
MustParseVersion("1.0.0"),
|
|
MustParseVersion("1.2.0"), // included because s2 matches "bar"
|
|
}
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
|
|
_, _, err = multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("baz"))
|
|
if want, ok := err.(ErrRegistryProviderNotKnown); !ok {
|
|
t.Fatalf("wrong error type:\ngot: %T\nwant: %T", err, want)
|
|
}
|
|
})
|
|
|
|
t.Run("provider not found", func(t *testing.T) {
|
|
s1 := NewMockSource(nil, nil)
|
|
s2 := NewMockSource(nil, nil)
|
|
multi := MultiSource{
|
|
{Source: s1},
|
|
{Source: s2},
|
|
}
|
|
|
|
_, _, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("foo"))
|
|
if err == nil {
|
|
t.Fatal("expected error, got success")
|
|
}
|
|
|
|
wantErr := `provider registry registry.terraform.io does not have a provider named registry.terraform.io/hashicorp/foo`
|
|
|
|
if err.Error() != wantErr {
|
|
t.Fatalf("wrong error.\ngot: %s\nwant: %s\n", err, wantErr)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("merging with warnings", func(t *testing.T) {
|
|
platform1 := Platform{OS: "amigaos", Arch: "m68k"}
|
|
platform2 := Platform{OS: "aros", Arch: "arm"}
|
|
s1 := NewMockSource([]PackageMeta{
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("bar"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform2,
|
|
),
|
|
},
|
|
map[addrs.Provider]Warnings{
|
|
addrs.NewDefaultProvider("bar"): {"WARNING!"},
|
|
},
|
|
)
|
|
s2 := NewMockSource([]PackageMeta{
|
|
FakePackageMeta(
|
|
addrs.NewDefaultProvider("bar"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
),
|
|
},
|
|
nil,
|
|
)
|
|
multi := MultiSource{
|
|
{Source: s1},
|
|
{Source: s2},
|
|
}
|
|
|
|
// AvailableVersions produces the union of all versions available
|
|
// across all of the sources.
|
|
got, warns, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("bar"))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
want := VersionList{
|
|
MustParseVersion("1.0.0"),
|
|
}
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
|
|
if len(warns) != 1 {
|
|
t.Fatalf("wrong number of warnings. Got %d, wanted 1", len(warns))
|
|
}
|
|
if warns[0] != "WARNING!" {
|
|
t.Fatalf("wrong warnings. Got %s, wanted \"WARNING!\"", warns[0])
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMultiSourcePackageMeta(t *testing.T) {
|
|
platform1 := Platform{OS: "amigaos", Arch: "m68k"}
|
|
platform2 := Platform{OS: "aros", Arch: "arm"}
|
|
|
|
// We'll use the Filename field of the fake PackageMetas we created above
|
|
// to create a difference between the packages in s1 and the ones in s2,
|
|
// so we can test where individual packages came from below.
|
|
fakeFilename := func(fn string, meta PackageMeta) PackageMeta {
|
|
meta.Filename = fn
|
|
return meta
|
|
}
|
|
|
|
onlyInS1 := fakeFilename("s1", FakePackageMeta(
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform2,
|
|
))
|
|
onlyInS2 := fakeFilename("s2", FakePackageMeta(
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.2.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
))
|
|
inBothS1 := fakeFilename("s1", FakePackageMeta(
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
))
|
|
inBothS2 := fakeFilename("s2", inBothS1)
|
|
s1 := NewMockSource([]PackageMeta{
|
|
inBothS1,
|
|
onlyInS1,
|
|
fakeFilename("s1", FakePackageMeta(
|
|
addrs.NewDefaultProvider("bar"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform2,
|
|
)),
|
|
},
|
|
nil,
|
|
)
|
|
s2 := NewMockSource([]PackageMeta{
|
|
inBothS2,
|
|
onlyInS2,
|
|
fakeFilename("s2", FakePackageMeta(
|
|
addrs.NewDefaultProvider("bar"),
|
|
MustParseVersion("1.0.0"),
|
|
VersionList{MustParseVersion("5.0")},
|
|
platform1,
|
|
)),
|
|
}, nil)
|
|
multi := MultiSource{
|
|
{Source: s1},
|
|
{Source: s2},
|
|
}
|
|
|
|
t.Run("only in s1", func(t *testing.T) {
|
|
got, err := multi.PackageMeta(
|
|
context.Background(),
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.0.0"),
|
|
platform2,
|
|
)
|
|
want := onlyInS1
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
})
|
|
t.Run("only in s2", func(t *testing.T) {
|
|
got, err := multi.PackageMeta(
|
|
context.Background(),
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.2.0"),
|
|
platform1,
|
|
)
|
|
want := onlyInS2
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
})
|
|
t.Run("in both", func(t *testing.T) {
|
|
got, err := multi.PackageMeta(
|
|
context.Background(),
|
|
addrs.NewDefaultProvider("foo"),
|
|
MustParseVersion("1.0.0"),
|
|
platform1,
|
|
)
|
|
want := inBothS1 // S1 "wins" because it's earlier in the MultiSource
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
|
|
// Make sure inBothS1 and inBothS2 really are different; if not then
|
|
// that's a test bug which we'd rather catch than have this test
|
|
// accidentally passing without actually checking anything.
|
|
if diff := cmp.Diff(inBothS1, inBothS2); diff == "" {
|
|
t.Fatalf("test bug: inBothS1 and inBothS2 are indistinguishable")
|
|
}
|
|
})
|
|
t.Run("in neither", func(t *testing.T) {
|
|
_, err := multi.PackageMeta(
|
|
context.Background(),
|
|
addrs.NewDefaultProvider("nonexist"),
|
|
MustParseVersion("1.0.0"),
|
|
platform1,
|
|
)
|
|
// This case reports "platform not supported" because it assumes that
|
|
// a caller would only pass to it package versions that were returned
|
|
// by a previousc all to AvailableVersions, and therefore a missing
|
|
// object ought to be valid provider/version but an unsupported
|
|
// platform.
|
|
if want, ok := err.(ErrPlatformNotSupported); !ok {
|
|
t.Fatalf("wrong error type:\ngot: %T\nwant: %T", err, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMultiSourceSelector(t *testing.T) {
|
|
emptySource := NewMockSource(nil, nil)
|
|
|
|
tests := map[string]struct {
|
|
Selector MultiSourceSelector
|
|
Provider addrs.Provider
|
|
WantMatch bool
|
|
}{
|
|
"default provider with no constraints": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
true,
|
|
},
|
|
"built-in provider with no constraints": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
},
|
|
addrs.NewBuiltInProvider("bar"),
|
|
true,
|
|
},
|
|
|
|
// Include constraints
|
|
"default provider with include constraint that matches it exactly": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
true,
|
|
},
|
|
"default provider with include constraint that matches it via type wildcard": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
true,
|
|
},
|
|
"default provider with include constraint that matches it via namespace wildcard": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("*/*"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
true,
|
|
},
|
|
"default provider with non-normalized include constraint that matches it via type wildcard": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("HashiCorp/*"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
true,
|
|
},
|
|
"built-in provider with exact include constraint that does not match it": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
|
|
},
|
|
addrs.NewBuiltInProvider("bar"),
|
|
false,
|
|
},
|
|
"built-in provider with type-wild include constraint that does not match it": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
|
|
},
|
|
addrs.NewBuiltInProvider("bar"),
|
|
false,
|
|
},
|
|
"built-in provider with namespace-wild include constraint that does not match it": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("*/*"),
|
|
},
|
|
// Doesn't match because builtin providers are in "terraform.io",
|
|
// but a pattern with no hostname is for registry.terraform.io.
|
|
addrs.NewBuiltInProvider("bar"),
|
|
false,
|
|
},
|
|
"built-in provider with include constraint that matches it via type wildcard": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("terraform.io/builtin/*"),
|
|
},
|
|
addrs.NewBuiltInProvider("bar"),
|
|
true,
|
|
},
|
|
|
|
// Exclude constraints
|
|
"default provider with exclude constraint that matches it exactly": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
false,
|
|
},
|
|
"default provider with exclude constraint that matches it via type wildcard": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
false,
|
|
},
|
|
"default provider with exact exclude constraint that doesn't match it": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/bar"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
true,
|
|
},
|
|
"default provider with non-normalized exclude constraint that matches it via type wildcard": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Exclude: mustParseMultiSourceMatchingPatterns("HashiCorp/*"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
false,
|
|
},
|
|
|
|
// Both include and exclude in a single selector
|
|
"default provider with exclude wildcard overriding include exact": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
|
|
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
false,
|
|
},
|
|
"default provider with exclude wildcard overriding irrelevant include exact": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/bar"),
|
|
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
false,
|
|
},
|
|
"default provider with exclude exact overriding include wildcard": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
|
|
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
false,
|
|
},
|
|
"default provider with irrelevant exclude exact overriding include wildcard": {
|
|
MultiSourceSelector{
|
|
Source: emptySource,
|
|
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
|
|
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/bar"),
|
|
},
|
|
addrs.NewDefaultProvider("foo"),
|
|
true,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Logf("include: %s", test.Selector.Include)
|
|
t.Logf("exclude: %s", test.Selector.Exclude)
|
|
t.Logf("provider: %s", test.Provider)
|
|
got := test.Selector.CanHandleProvider(test.Provider)
|
|
want := test.WantMatch
|
|
if got != want {
|
|
t.Errorf("wrong result %t; want %t", got, want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func mustParseMultiSourceMatchingPatterns(strs ...string) MultiSourceMatchingPatterns {
|
|
ret, err := ParseMultiSourceMatchingPatterns(strs)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ret
|
|
}
|