From 41013e1209c3f05b9cae80a40f8fa95be33dd835 Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk Date: Thu, 5 Oct 2023 13:16:46 +0100 Subject: [PATCH] Add initial SemVer upgrade support OLM will now use SemVer to determine next upgrade. In this iteration we only support upgrade within the same major versions: e.g 1.0.1 can be upgraded to 1.0.2 or 1.3.2, but not 2.0.0. Signed-off-by: Mikalai Radchuk --- .../variablesources/installed_package.go | 34 ++++++++++++++++++- .../variablesources/installed_package_test.go | 21 ++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/internal/resolution/variablesources/installed_package.go b/internal/resolution/variablesources/installed_package.go index 03a7eeafa..7f73ec495 100644 --- a/internal/resolution/variablesources/installed_package.go +++ b/internal/resolution/variablesources/installed_package.go @@ -5,6 +5,7 @@ import ( "fmt" "sort" + mmsemver "github.com/Masterminds/semver/v3" "github.com/operator-framework/deppy/pkg/deppy" "github.com/operator-framework/deppy/pkg/deppy/input" @@ -12,6 +13,7 @@ import ( catalogfilter "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter" catalogsort "github.com/operator-framework/operator-controller/internal/catalogmetadata/sort" "github.com/operator-framework/operator-controller/internal/resolution/variables" + "github.com/operator-framework/operator-controller/pkg/features" ) var _ input.VariableSource = &InstalledPackageVariableSource{} @@ -59,10 +61,15 @@ func (r *InstalledPackageVariableSource) notFoundError() error { } func NewInstalledPackageVariableSource(catalogClient BundleProvider, bundleImage string) (*InstalledPackageVariableSource, error) { + successors := legacySemanticsSuccessors + if features.OperatorControllerFeatureGate.Enabled(features.ForceSemverUpgradeConstraints) { + successors = semverSuccessors + } + return &InstalledPackageVariableSource{ catalogClient: catalogClient, bundleImage: bundleImage, - successors: legacySemanticsSuccessors, + successors: successors, }, nil } @@ -83,3 +90,28 @@ func legacySemanticsSuccessors(allBundles []*catalogmetadata.Bundle, installedBu return upgradeEdges, nil } + +// semverSuccessors returns successors based on Semver. +// Successors will not include versions outside the major version of the +// installed bundle as major version is intended to indicate breaking changes. +func semverSuccessors(allBundles []*catalogmetadata.Bundle, installedBundle *catalogmetadata.Bundle) ([]*catalogmetadata.Bundle, error) { + currentVersion, err := installedBundle.Version() + if err != nil { + return nil, err + } + + // Based on current version create a caret range comparison constraint + // to allow only major and patch version as successors and exclude current version. + constraintStr := fmt.Sprintf("^%s, != %s", currentVersion.String(), currentVersion.String()) + wantedVersionRangeConstraint, err := mmsemver.NewConstraint(constraintStr) + if err != nil { + return nil, err + } + + upgradeEdges := catalogfilter.Filter(allBundles, catalogfilter.InMastermindsSemverRange(wantedVersionRangeConstraint)) + sort.SliceStable(upgradeEdges, func(i, j int) bool { + return catalogsort.ByVersion(upgradeEdges[i], upgradeEdges[j]) + }) + + return upgradeEdges, nil +} diff --git a/internal/resolution/variablesources/installed_package_test.go b/internal/resolution/variablesources/installed_package_test.go index bbc06f89f..5fb68b069 100644 --- a/internal/resolution/variablesources/installed_package_test.go +++ b/internal/resolution/variablesources/installed_package_test.go @@ -120,6 +120,27 @@ func TestInstalledPackageVariableSource(t *testing.T) { const bundleImage = "registry.io/repo/test-package@v2.0.0" fakeCatalogClient := testutil.NewFakeCatalogClient(bundleList) + t.Run("with ForceSemverUpgradeConstraints feature gate enabled", func(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.ForceSemverUpgradeConstraints, true)() + + ipvs, err := variablesources.NewInstalledPackageVariableSource(&fakeCatalogClient, bundleImage) + assert.NoError(t, err) + + variables, err := ipvs.GetVariables(context.TODO()) + assert.NoError(t, err) + assert.Len(t, variables, 1) + packageVariable, ok := variables[0].(*olmvariables.InstalledPackageVariable) + assert.True(t, ok) + assert.Equal(t, deppy.IdentifierFromString("installed package test-package"), packageVariable.Identifier()) + + // ensure bundles are in version order (high to low) + bundles := packageVariable.Bundles() + assert.Len(t, bundles, 3) + assert.Equal(t, "test-package.v2.2.0", packageVariable.Bundles()[0].Name) + assert.Equal(t, "test-package.v2.1.0", packageVariable.Bundles()[1].Name) + assert.Equal(t, "test-package.v2.0.0", packageVariable.Bundles()[2].Name) + }) + t.Run("with ForceSemverUpgradeConstraints feature gate disabled", func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.ForceSemverUpgradeConstraints, false)()