Skip to content

Commit 2ac8ff8

Browse files
author
Mikalai Radchuk
committed
Move bundle uniqueness code into a func
Signed-off-by: Mikalai Radchuk <[email protected]>
1 parent 5536692 commit 2ac8ff8

File tree

3 files changed

+143
-30
lines changed

3 files changed

+143
-30
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/Masterminds/semver/v3 v3.2.1
77
github.com/blang/semver/v4 v4.0.0
88
github.com/go-logr/logr v1.3.0
9+
github.com/google/go-cmp v0.6.0
910
github.com/onsi/ginkgo/v2 v2.13.0
1011
github.com/onsi/gomega v1.29.0
1112
github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42
@@ -72,7 +73,6 @@ require (
7273
github.com/golang/protobuf v1.5.3 // indirect
7374
github.com/google/cel-go v0.12.6 // indirect
7475
github.com/google/gnostic v0.5.7-v3refs // indirect
75-
github.com/google/go-cmp v0.6.0 // indirect
7676
github.com/google/gofuzz v1.2.0 // indirect
7777
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
7878
github.com/google/uuid v1.3.0 // indirect

internal/resolution/variablesources/bundle_uniqueness.go

+45-29
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,53 @@ import (
44
"context"
55
"fmt"
66

7+
"k8s.io/apimachinery/pkg/util/sets"
8+
79
"github.com/operator-framework/deppy/pkg/deppy"
810
"github.com/operator-framework/deppy/pkg/deppy/input"
9-
"k8s.io/apimachinery/pkg/util/sets"
1011

1112
"github.com/operator-framework/operator-controller/internal/catalogmetadata"
1213
olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables"
1314
)
1415

15-
var _ input.VariableSource = &CRDUniquenessConstraintsVariableSource{}
16+
// MakeBundleUniquenessVariables produces variables that constrain
17+
// the solution to at most 1 bundle per package.
18+
// These variables guarantee that no two versions of
19+
// the same package are running at the same time.
20+
func MakeBundleUniquenessVariables(bundleVariables []*olmvariables.BundleVariable) []*olmvariables.BundleUniquenessVariable {
21+
result := []*olmvariables.BundleUniquenessVariable{}
22+
23+
bundleIDs := sets.Set[deppy.Identifier]{}
24+
packageOrder := []string{}
25+
bundleOrder := map[string][]deppy.Identifier{}
26+
for _, bundleVariable := range bundleVariables {
27+
bundles := make([]*catalogmetadata.Bundle, 0, 1+len(bundleVariable.Dependencies()))
28+
bundles = append(bundles, bundleVariable.Bundle())
29+
bundles = append(bundles, bundleVariable.Dependencies()...)
30+
for _, bundle := range bundles {
31+
id := olmvariables.BundleVariableID(bundle)
32+
// get bundleID package and update map
33+
packageName := bundle.Package
34+
35+
if _, ok := bundleOrder[packageName]; !ok {
36+
packageOrder = append(packageOrder, packageName)
37+
}
38+
39+
if !bundleIDs.Has(id) {
40+
bundleIDs.Insert(id)
41+
bundleOrder[packageName] = append(bundleOrder[packageName], id)
42+
}
43+
}
44+
}
45+
46+
// create global constraint variables
47+
for _, packageName := range packageOrder {
48+
varID := deppy.IdentifierFromString(fmt.Sprintf("%s package uniqueness", packageName))
49+
result = append(result, olmvariables.NewBundleUniquenessVariable(varID, bundleOrder[packageName]...))
50+
}
51+
52+
return result
53+
}
1654

1755
// CRDUniquenessConstraintsVariableSource produces variables that constraint the solution to
1856
// 1. at most 1 bundle per package
@@ -41,39 +79,17 @@ func (g *CRDUniquenessConstraintsVariableSource) GetVariables(ctx context.Contex
4179
return nil, err
4280
}
4381

44-
// todo(perdasilva): better handle cases where a provided gvk is not found
45-
// not all packages will necessarily export a CRD
46-
47-
bundleIDs := sets.Set[deppy.Identifier]{}
48-
packageOrder := []string{}
49-
bundleOrder := map[string][]deppy.Identifier{}
82+
bundleVariables := []*olmvariables.BundleVariable{}
5083
for _, variable := range variables {
5184
switch v := variable.(type) {
5285
case *olmvariables.BundleVariable:
53-
bundles := []*catalogmetadata.Bundle{v.Bundle()}
54-
bundles = append(bundles, v.Dependencies()...)
55-
for _, bundle := range bundles {
56-
id := olmvariables.BundleVariableID(bundle)
57-
// get bundleID package and update map
58-
packageName := bundle.Package
59-
60-
if _, ok := bundleOrder[packageName]; !ok {
61-
packageOrder = append(packageOrder, packageName)
62-
}
63-
64-
if !bundleIDs.Has(id) {
65-
bundleIDs.Insert(id)
66-
bundleOrder[packageName] = append(bundleOrder[packageName], id)
67-
}
68-
}
86+
bundleVariables = append(bundleVariables, v)
6987
}
7088
}
7189

72-
// create global constraint variables
73-
for _, packageName := range packageOrder {
74-
varID := deppy.IdentifierFromString(fmt.Sprintf("%s package uniqueness", packageName))
75-
variables = append(variables, olmvariables.NewBundleUniquenessVariable(varID, bundleOrder[packageName]...))
90+
bundleUniqueness := MakeBundleUniquenessVariables(bundleVariables)
91+
for _, v := range bundleUniqueness {
92+
variables = append(variables, v)
7693
}
77-
7894
return variables, nil
7995
}

internal/resolution/variablesources/bundle_uniqueness_test.go

+97
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"testing"
78

9+
"github.com/google/go-cmp/cmp"
810
. "github.com/onsi/ginkgo/v2"
911
. "github.com/onsi/gomega"
12+
"github.com/stretchr/testify/require"
1013

1114
"github.com/operator-framework/deppy/pkg/deppy"
15+
"github.com/operator-framework/deppy/pkg/deppy/constraint"
16+
"github.com/operator-framework/deppy/pkg/deppy/input"
1217
"github.com/operator-framework/operator-registry/alpha/declcfg"
1318
"github.com/operator-framework/operator-registry/alpha/property"
1419

@@ -17,6 +22,98 @@ import (
1722
"github.com/operator-framework/operator-controller/internal/resolution/variablesources"
1823
)
1924

25+
func TestMakeBundleUniquenessVariables(t *testing.T) {
26+
const fakeCatalogName = "fake-catalog"
27+
channel := catalogmetadata.Channel{Channel: declcfg.Channel{Name: "stable"}}
28+
bundleSet := map[string]*catalogmetadata.Bundle{
29+
"test-package.v1.0.0": {
30+
Bundle: declcfg.Bundle{
31+
Name: "test-package.v1.0.0",
32+
Package: "test-package",
33+
Properties: []property.Property{
34+
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)},
35+
{Type: property.TypePackageRequired, Value: json.RawMessage(`{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}`)},
36+
},
37+
},
38+
CatalogName: fakeCatalogName,
39+
InChannels: []*catalogmetadata.Channel{&channel},
40+
},
41+
"test-package.v1.0.1": {
42+
Bundle: declcfg.Bundle{
43+
Name: "test-package.v1.0.1",
44+
Package: "test-package",
45+
Properties: []property.Property{
46+
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.1"}`)},
47+
{Type: property.TypePackageRequired, Value: json.RawMessage(`{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}`)},
48+
},
49+
},
50+
CatalogName: fakeCatalogName,
51+
InChannels: []*catalogmetadata.Channel{&channel},
52+
},
53+
54+
"some-package.v1.0.0": {
55+
Bundle: declcfg.Bundle{
56+
Name: "some-package.v1.0.0",
57+
Package: "some-package",
58+
Properties: []property.Property{
59+
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "1.0.0"}`)},
60+
},
61+
},
62+
CatalogName: fakeCatalogName,
63+
InChannels: []*catalogmetadata.Channel{&channel},
64+
},
65+
}
66+
67+
// Input into the testable function must include more than one bundle
68+
// from the same package to ensure that the function
69+
// enforces uniqueness correctly.
70+
// We also need at least one bundle variable to have a dependency
71+
// on another package. This will help to make sure that the function
72+
// also creates uniqueness variables for dependencies.
73+
bundleVariables := []*olmvariables.BundleVariable{
74+
olmvariables.NewBundleVariable(
75+
bundleSet["test-package.v1.0.0"],
76+
[]*catalogmetadata.Bundle{
77+
bundleSet["some-package.v1.0.0"],
78+
},
79+
),
80+
olmvariables.NewBundleVariable(
81+
bundleSet["test-package.v1.0.1"],
82+
[]*catalogmetadata.Bundle{
83+
bundleSet["some-package.v1.0.0"],
84+
},
85+
),
86+
}
87+
88+
variables := variablesources.MakeBundleUniquenessVariables(bundleVariables)
89+
90+
// Each package in the input must have own uniqueness variable.
91+
// Each variable expected to have one constraint enforcing at most one
92+
// of the involved bundles to be allowed in the solution
93+
expectedVariables := []*olmvariables.BundleUniquenessVariable{
94+
{
95+
SimpleVariable: input.NewSimpleVariable(
96+
"test-package package uniqueness",
97+
constraint.AtMost(
98+
1,
99+
deppy.Identifier("fake-catalog-test-package-test-package.v1.0.0"),
100+
deppy.Identifier("fake-catalog-test-package-test-package.v1.0.1"),
101+
),
102+
),
103+
},
104+
{
105+
SimpleVariable: input.NewSimpleVariable(
106+
"some-package package uniqueness",
107+
constraint.AtMost(
108+
1,
109+
deppy.Identifier("fake-catalog-some-package-some-package.v1.0.0"),
110+
),
111+
),
112+
},
113+
}
114+
require.Empty(t, cmp.Diff(variables, expectedVariables, cmp.AllowUnexported(input.SimpleVariable{}, constraint.AtMostConstraint{})))
115+
}
116+
20117
var channel = catalogmetadata.Channel{Channel: declcfg.Channel{Name: "stable"}}
21118
var bundleSet = map[string]*catalogmetadata.Bundle{
22119
// required package bundles

0 commit comments

Comments
 (0)