Skip to content

Commit 4a0d099

Browse files
author
Mikalai Radchuk
committed
fixup! fixup! PoC: refactor variable sources
Signed-off-by: Mikalai Radchuk <[email protected]>
1 parent cd2ab54 commit 4a0d099

File tree

2 files changed

+149
-2
lines changed

2 files changed

+149
-2
lines changed

internal/resolution/variablesources/olm.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
catalogfilter "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter"
1919
catalogsort "github.com/operator-framework/operator-controller/internal/catalogmetadata/sort"
2020
olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables"
21+
"github.com/operator-framework/operator-controller/pkg/features"
2122
)
2223

2324
var _ input.VariableSource = &OLMVariableSource{}
@@ -142,8 +143,10 @@ func InstalledPackageVariables(
142143
allBundles []*catalogmetadata.Bundle,
143144
bundleDeployments []rukpakv1alpha1.BundleDeployment,
144145
) ([]*olmvariables.InstalledPackageVariable, error) {
145-
// TODO: Switch between legacy and semver based on feature flag once semver implemented
146146
var successors successorsFunc = legacySemanticsSuccessors
147+
if features.OperatorControllerFeatureGate.Enabled(features.ForceSemverUpgradeConstraints) {
148+
successors = semverSuccessors
149+
}
147150

148151
result := make([]*olmvariables.InstalledPackageVariable, 0, len(bundleDeployments))
149152
processed := sets.Set[string]{}
@@ -205,6 +208,31 @@ func legacySemanticsSuccessors(allBundles []*catalogmetadata.Bundle, installedBu
205208
return upgradeEdges, nil
206209
}
207210

211+
// semverSuccessors returns successors based on Semver.
212+
// Successors will not include versions outside the major version of the
213+
// installed bundle as major version is intended to indicate breaking changes.
214+
func semverSuccessors(allBundles []*catalogmetadata.Bundle, installedBundle *catalogmetadata.Bundle) ([]*catalogmetadata.Bundle, error) {
215+
currentVersion, err := installedBundle.Version()
216+
if err != nil {
217+
return nil, err
218+
}
219+
220+
// Based on current version create a caret range comparison constraint
221+
// to allow only minor and patch version as successors and exclude current version.
222+
constraintStr := fmt.Sprintf("^%s, != %s", currentVersion.String(), currentVersion.String())
223+
wantedVersionRangeConstraint, err := mmsemver.NewConstraint(constraintStr)
224+
if err != nil {
225+
return nil, err
226+
}
227+
228+
upgradeEdges := catalogfilter.Filter(allBundles, catalogfilter.InMastermindsSemverRange(wantedVersionRangeConstraint))
229+
sort.SliceStable(upgradeEdges, func(i, j int) bool {
230+
return catalogsort.ByVersion(upgradeEdges[i], upgradeEdges[j])
231+
})
232+
233+
return upgradeEdges, nil
234+
}
235+
208236
func BundleVariables(
209237
allBundles []*catalogmetadata.Bundle,
210238
requiredPackages []*olmvariables.RequiredPackageVariable,

internal/resolution/variablesources/olm_test.go

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,27 @@ func TestInstalledPackageVariables(t *testing.T) {
140140
Name: "stable",
141141
Entries: []declcfg.ChannelEntry{
142142
{
143-
Name: "test-package.v1.0.0",
143+
Name: "test-package.v0.0.1",
144+
},
145+
{
146+
Name: "test-package.v0.0.2",
147+
Replaces: "test-package.v0.0.1",
148+
},
149+
{
150+
Name: "test-package.v0.1.0",
151+
Replaces: "test-package.v0.0.2",
152+
},
153+
{
154+
Name: "test-package.v0.1.1",
155+
Replaces: "test-package.v0.1.0",
156+
},
157+
{
158+
Name: "test-package.v0.2.0",
159+
Replaces: "test-package.v0.1.1",
160+
},
161+
{
162+
Name: "test-package.v1.0.0",
163+
Replaces: "test-package.v0.2.0",
144164
},
145165
{
146166
Name: "test-package.v2.0.0",
@@ -169,6 +189,51 @@ func TestInstalledPackageVariables(t *testing.T) {
169189
},
170190
}}
171191
allBundles := []*catalogmetadata.Bundle{
192+
{Bundle: declcfg.Bundle{
193+
Name: "test-package.v0.0.1",
194+
Package: "test-package",
195+
Image: "registry.io/repo/[email protected]",
196+
Properties: []property.Property{
197+
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "0.0.1"}`)},
198+
}},
199+
InChannels: []*catalogmetadata.Channel{&channel},
200+
},
201+
{Bundle: declcfg.Bundle{
202+
Name: "test-package.v0.0.2",
203+
Package: "test-package",
204+
Image: "registry.io/repo/[email protected]",
205+
Properties: []property.Property{
206+
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "0.0.2"}`)},
207+
}},
208+
InChannels: []*catalogmetadata.Channel{&channel},
209+
},
210+
{Bundle: declcfg.Bundle{
211+
Name: "test-package.v0.1.0",
212+
Package: "test-package",
213+
Image: "registry.io/repo/[email protected]",
214+
Properties: []property.Property{
215+
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "0.1.0"}`)},
216+
}},
217+
InChannels: []*catalogmetadata.Channel{&channel},
218+
},
219+
{Bundle: declcfg.Bundle{
220+
Name: "test-package.v0.1.1",
221+
Package: "test-package",
222+
Image: "registry.io/repo/[email protected]",
223+
Properties: []property.Property{
224+
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "0.1.1"}`)},
225+
}},
226+
InChannels: []*catalogmetadata.Channel{&channel},
227+
},
228+
{Bundle: declcfg.Bundle{
229+
Name: "test-package.v0.2.0",
230+
Package: "test-package",
231+
Image: "registry.io/repo/[email protected]",
232+
Properties: []property.Property{
233+
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "0.2.0"}`)},
234+
}},
235+
InChannels: []*catalogmetadata.Channel{&channel},
236+
},
172237
{Bundle: declcfg.Bundle{
173238
Name: "test-package.v1.0.0",
174239
Package: "test-package",
@@ -259,6 +324,60 @@ func TestInstalledPackageVariables(t *testing.T) {
259324
return bundleDeployments
260325
}
261326

327+
t.Run("with ForceSemverUpgradeConstraints feature gate enabled", func(t *testing.T) {
328+
defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.ForceSemverUpgradeConstraints, true)()
329+
330+
t.Run("with non-zero major version", func(t *testing.T) {
331+
const bundleImage = "registry.io/repo/[email protected]"
332+
installedPackages, err := variablesources.InstalledPackageVariables(allBundles, fakeBundleDeployments(bundleImage))
333+
require.NoError(t, err)
334+
335+
require.Len(t, installedPackages, 1)
336+
packageVariable := installedPackages[0]
337+
assert.Equal(t, deppy.IdentifierFromString("installed package test-package"), packageVariable.Identifier())
338+
339+
// ensure bundles are in version order (high to low)
340+
bundles := packageVariable.Bundles()
341+
require.Len(t, bundles, 3)
342+
assert.Equal(t, "test-package.v2.2.0", packageVariable.Bundles()[0].Name)
343+
assert.Equal(t, "test-package.v2.1.0", packageVariable.Bundles()[1].Name)
344+
assert.Equal(t, "test-package.v2.0.0", packageVariable.Bundles()[2].Name)
345+
})
346+
347+
t.Run("with zero major version", func(t *testing.T) {
348+
t.Run("with zero minor version", func(t *testing.T) {
349+
const bundleImage = "registry.io/repo/[email protected]"
350+
installedPackages, err := variablesources.InstalledPackageVariables(allBundles, fakeBundleDeployments(bundleImage))
351+
require.NoError(t, err)
352+
353+
require.Len(t, installedPackages, 1)
354+
packageVariable := installedPackages[0]
355+
assert.Equal(t, deppy.IdentifierFromString("installed package test-package"), packageVariable.Identifier())
356+
357+
// No upgrades are allowed in major version zero when minor version is also zero
358+
bundles := packageVariable.Bundles()
359+
require.Len(t, bundles, 1)
360+
assert.Equal(t, "test-package.v0.0.1", packageVariable.Bundles()[0].Name)
361+
})
362+
363+
t.Run("with non-zero minor version", func(t *testing.T) {
364+
const bundleImage = "registry.io/repo/[email protected]"
365+
installedPackages, err := variablesources.InstalledPackageVariables(allBundles, fakeBundleDeployments(bundleImage))
366+
require.NoError(t, err)
367+
368+
require.Len(t, installedPackages, 1)
369+
packageVariable := installedPackages[0]
370+
assert.Equal(t, deppy.IdentifierFromString("installed package test-package"), packageVariable.Identifier())
371+
372+
// Patch version upgrades are allowed, but not minor upgrades
373+
bundles := packageVariable.Bundles()
374+
require.Len(t, bundles, 2)
375+
assert.Equal(t, "test-package.v0.1.1", packageVariable.Bundles()[0].Name)
376+
assert.Equal(t, "test-package.v0.1.0", packageVariable.Bundles()[1].Name)
377+
})
378+
})
379+
})
380+
262381
t.Run("with ForceSemverUpgradeConstraints feature gate disabled", func(t *testing.T) {
263382
defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.ForceSemverUpgradeConstraints, false)()
264383

0 commit comments

Comments
 (0)