diff --git a/CHANGELOG.md b/CHANGELOG.md index 4152d31283..0c25616b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ ENHANCEMENTS: * Changes to encryption configuration now auto-apply the migration ([#2232](https://github.com/opentofu/opentofu/pull/2232)) * References to vars, data, etc. are now usable in variable validation ([#2216](https://github.com/opentofu/opentofu/pull/2216)) * `AzureRM` backend now support `timeout_seconds` with default timeout of 300 seconds ([#2263](https://github.com/opentofu/opentofu/pull/2263)) +* Adds warning about provider version `0.0.0` ([#2281](https://github.com/opentofu/opentofu/pull/2281)) BUG FIXES: * `templatefile` no longer crashes if the given filename is derived from a sensitive value. ([#1801](https://github.com/opentofu/opentofu/issues/1801)) diff --git a/internal/getproviders/filesystem_mirror_source.go b/internal/getproviders/filesystem_mirror_source.go index 46a182609c..ccea4934fe 100644 --- a/internal/getproviders/filesystem_mirror_source.go +++ b/internal/getproviders/filesystem_mirror_source.go @@ -7,7 +7,9 @@ package getproviders import ( "context" + "fmt" + "github.com/apparentlymart/go-versions/versions" "github.com/opentofu/opentofu/internal/addrs" ) @@ -54,7 +56,16 @@ func (s *FilesystemMirrorSource) AvailableVersions(ctx context.Context, provider ret = append(ret, v) } ret.Sort() - return ret, nil, nil + // Check the existence of provider version 0.0.0 in the filesystem and warn the user about it + // If it exists, it will be the first element in the sorted list + var warnings Warnings + if len(ret) > 0 && ret[0] == versions.Unspecified { + warning := fmt.Sprintf("Provider %s has an unspecified (0.0.0) version available in the filesystem mirror, source at %s. It will not be used. \n"+ + "If the version 0.0.0 is intended to represent a non-published provider, consider using dev_overrides - https://opentofu.org/docs/cli/config/config-file/#development-overrides-for-provider-developers", + provider, s.baseDir) + warnings = append(warnings, warning) + } + return ret, warnings, nil } // PackageMeta checks to see if the source's base directory contains a diff --git a/internal/getproviders/filesystem_mirror_source_test.go b/internal/getproviders/filesystem_mirror_source_test.go index a530aee675..24ecd899f1 100644 --- a/internal/getproviders/filesystem_mirror_source_test.go +++ b/internal/getproviders/filesystem_mirror_source_test.go @@ -7,6 +7,7 @@ package getproviders import ( "context" + "strings" "testing" "github.com/apparentlymart/go-versions/versions" @@ -125,6 +126,30 @@ func TestFilesystemMirrorSourceAvailableVersions(t *testing.T) { } } +func TestFilesystemMirrorSourceAvailableVersions_Unspecified(t *testing.T) { + unspecifiedProvider := addrs.Provider{ + Hostname: svchost.Hostname("registry.opentofu.org"), + Namespace: "testnamespace", + Type: "unspecified", + } + source := NewFilesystemMirrorSource("testdata/filesystem-mirror-unspecified") + got, warn, err := source.AvailableVersions(context.Background(), unspecifiedProvider) + if err != nil { + t.Fatal(err) + } + // Check that we got the unspecified version + if len(got) != 1 || got[0] != versions.Unspecified { + t.Fatalf("expected unspecified version, got %v", got) + } + // We should have unspecified (0.0.0) version warning + if len(warn) != 1 { + t.Fatalf("expected 1 warning, got %v", warn) + } + warningBit := "unspecified (0.0.0) version available in the filesystem mirror" + if !strings.Contains(warn[0], warningBit) { + t.Fatalf("expected warning to contain %q, got %q", warningBit, warn[0]) + } +} func TestFilesystemMirrorSourcePackageMeta(t *testing.T) { t.Run("available platform", func(t *testing.T) { source := NewFilesystemMirrorSource("testdata/filesystem-mirror") diff --git a/internal/getproviders/testdata/filesystem-mirror-unspecified/registry.opentofu.org/testnamespace/unspecified/0.0.0/linux_amd64/unspecified-provider b/internal/getproviders/testdata/filesystem-mirror-unspecified/registry.opentofu.org/testnamespace/unspecified/0.0.0/linux_amd64/unspecified-provider new file mode 100644 index 0000000000..daa9e3509f --- /dev/null +++ b/internal/getproviders/testdata/filesystem-mirror-unspecified/registry.opentofu.org/testnamespace/unspecified/0.0.0/linux_amd64/unspecified-provider @@ -0,0 +1 @@ +# This is just a placeholder file for discovery testing, not a real provider plugin. diff --git a/internal/providercache/installer.go b/internal/providercache/installer.go index 0662ab1fad..6490880aee 100644 --- a/internal/providercache/installer.go +++ b/internal/providercache/installer.go @@ -291,10 +291,18 @@ NeedProvider: // that context will fail immediately anyway. return nil, err } - if cb := evts.QueryPackagesBegin; cb != nil { cb(provider, reqs[provider], locked[provider]) } + // Version 0.0.0 not supported + if err := checkUnspecifiedVersion(acceptableVersions); err != nil { + errs[provider] = err + if cb := evts.QueryPackagesFailure; cb != nil { + cb(provider, err) + } + continue + } + available, warnings, err := i.source.AvailableVersions(ctx, provider) if err != nil { errs[provider] = err @@ -736,6 +744,15 @@ NeedProvider: return locks, nil } +// checkUnspecifiedVersion Check the presence of version 0.0.0 and return an error with a tip +func checkUnspecifiedVersion(acceptableVersions versions.Set) error { + if !acceptableVersions.Exactly(versions.Unspecified) { + return nil + } + tip := "If the version 0.0.0 is intended to represent a non-published provider, consider using dev_overrides - https://opentofu.org/docs/cli/config/config-file/#development-overrides-for-provider-developers" + return fmt.Errorf("0.0.0 is not a valid provider version. \n%s", tip) +} + // InstallMode customizes the details of how an install operation treats // providers that have versions already cached in the target directory. type InstallMode rune diff --git a/internal/providercache/installer_test.go b/internal/providercache/installer_test.go index b7f24199de..5decdad7b2 100644 --- a/internal/providercache/installer_test.go +++ b/internal/providercache/installer_test.go @@ -2315,6 +2315,12 @@ func TestEnsureProviderVersions_local_source(t *testing.T) { wantHash: getproviders.NilHash, // installation fails for a provider with no executable err: "provider binary not found: could not find executable file starting with terraform-provider-executable", }, + "unspecified-version": { + provider: "null", + version: "0.0.0", + wantHash: getproviders.NilHash, + err: "0.0.0 is not a valid provider version. \nIf the version 0.0.0 is intended to represent a non-published provider, consider using dev_overrides - https://opentofu.org/docs/cli/config/config-file/#development-overrides-for-provider-developers", + }, } for name, test := range tests {