Skip to content

Commit

Permalink
Move bundle uniqueness code into a func
Browse files Browse the repository at this point in the history
Signed-off-by: Mikalai Radchuk <mradchuk@redhat.com>
  • Loading branch information
m1kola committed Oct 31, 2023
1 parent f8b2142 commit 60ef728
Show file tree
Hide file tree
Showing 3 changed files with 312 additions and 28 deletions.
75 changes: 47 additions & 28 deletions internal/resolution/variablesources/bundle_uniqueness.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,52 @@ import (
"context"
"fmt"

"k8s.io/apimachinery/pkg/util/sets"

"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/deppy/pkg/deppy/input"
"k8s.io/apimachinery/pkg/util/sets"

"github.com/operator-framework/operator-controller/internal/catalogmetadata"
olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables"
)

var _ input.VariableSource = &CRDUniquenessConstraintsVariableSource{}
// MakeBundleUniquenessVariables produces variables that constrain
// the solution to at most 1 bundle per package.
// These variables guarantee that no two versions of
// the same package are running at the same time.
func MakeBundleUniquenessVariables(bundleVariables []*olmvariables.BundleVariable) ([]*olmvariables.BundleUniquenessVariable, error) {
result := []*olmvariables.BundleUniquenessVariable{}

bundleIDs := sets.Set[deppy.Identifier]{}
packageOrder := []string{}
bundleOrder := map[string][]deppy.Identifier{}
for _, bundleVariable := range bundleVariables {
bundles := []*catalogmetadata.Bundle{bundleVariable.Bundle()}
bundles = append(bundles, bundleVariable.Dependencies()...)
for _, bundle := range bundles {
id := olmvariables.BundleVariableID(bundle)
// get bundleID package and update map
packageName := bundle.Package

if _, ok := bundleOrder[packageName]; !ok {
packageOrder = append(packageOrder, packageName)
}

if !bundleIDs.Has(id) {
bundleIDs.Insert(id)
bundleOrder[packageName] = append(bundleOrder[packageName], id)
}
}
}

// create global constraint variables
for _, packageName := range packageOrder {
varID := deppy.IdentifierFromString(fmt.Sprintf("%s package uniqueness", packageName))
result = append(result, olmvariables.NewBundleUniquenessVariable(varID, bundleOrder[packageName]...))
}

return result, nil
}

// CRDUniquenessConstraintsVariableSource produces variables that constraint the solution to
// 1. at most 1 bundle per package
Expand Down Expand Up @@ -41,39 +78,21 @@ func (g *CRDUniquenessConstraintsVariableSource) GetVariables(ctx context.Contex
return nil, err
}

// todo(perdasilva): better handle cases where a provided gvk is not found
// not all packages will necessarily export a CRD

bundleIDs := sets.Set[deppy.Identifier]{}
packageOrder := []string{}
bundleOrder := map[string][]deppy.Identifier{}
bundleVariables := []*olmvariables.BundleVariable{}
for _, variable := range variables {
switch v := variable.(type) {
case *olmvariables.BundleVariable:
bundles := []*catalogmetadata.Bundle{v.Bundle()}
bundles = append(bundles, v.Dependencies()...)
for _, bundle := range bundles {
id := olmvariables.BundleVariableID(bundle)
// get bundleID package and update map
packageName := bundle.Package

if _, ok := bundleOrder[packageName]; !ok {
packageOrder = append(packageOrder, packageName)
}

if !bundleIDs.Has(id) {
bundleIDs.Insert(id)
bundleOrder[packageName] = append(bundleOrder[packageName], id)
}
}
bundleVariables = append(bundleVariables, v)
}
}

// create global constraint variables
for _, packageName := range packageOrder {
varID := deppy.IdentifierFromString(fmt.Sprintf("%s package uniqueness", packageName))
variables = append(variables, olmvariables.NewBundleUniquenessVariable(varID, bundleOrder[packageName]...))
bundleUniqueness, err := MakeBundleUniquenessVariables(bundleVariables)
if err != nil {
return nil, err
}

for _, v := range bundleUniqueness {
variables = append(variables, v)
}
return variables, nil
}
252 changes: 252 additions & 0 deletions internal/resolution/variablesources/bundle_uniqueness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"context"
"encoding/json"
"fmt"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/operator-registry/alpha/declcfg"
Expand All @@ -17,6 +20,255 @@ import (
"github.com/operator-framework/operator-controller/internal/resolution/variablesources"
)

func TestMakeBundleUniquenessVariables(t *testing.T) {
channel := catalogmetadata.Channel{Channel: declcfg.Channel{Name: "stable"}}
bundleSet := map[string]*catalogmetadata.Bundle{
// required package bundles
"bundle-1": {Bundle: declcfg.Bundle{
Name: "bundle-1",
Package: "test-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)},
{Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"bit.io","kind":"Bit","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-2": {Bundle: declcfg.Bundle{
Name: "bundle-2",
Package: "test-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)},
{Type: property.TypeGVKRequired, Value: json.RawMessage(`{"group":"foo.io","kind":"Foo","version":"v1"}`)},
{Type: property.TypePackageRequired, Value: json.RawMessage(`{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`{"group":"bit.io","kind":"Bit","version":"v1"}`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},

// dependencies
"bundle-3": {Bundle: declcfg.Bundle{
Name: "bundle-3",
Package: "some-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "1.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"fiz.io","kind":"Fiz","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-4": {Bundle: declcfg.Bundle{
Name: "bundle-4",
Package: "some-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "1.5.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"fiz.io","kind":"Fiz","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-5": {Bundle: declcfg.Bundle{
Name: "bundle-5",
Package: "some-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "2.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"fiz.io","kind":"Fiz","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-6": {Bundle: declcfg.Bundle{
Name: "bundle-6",
Package: "some-other-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-other-package", "version": "1.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-7": {Bundle: declcfg.Bundle{
Name: "bundle-7",
Package: "some-other-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-other-package", "version": "1.5.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`{"group":"foo.io","kind":"Foo","version":"v1"}`)},
{Type: property.TypeGVKRequired, Value: json.RawMessage(`{"group":"bar.io","kind":"Bar","version":"v1"}`)},
{Type: property.TypePackageRequired, Value: json.RawMessage(`{"packageName": "another-package", "versionRange": "< 2.0.0"}`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},

// dependencies of dependencies
"bundle-8": {Bundle: declcfg.Bundle{
Name: "bundle-8",
Package: "another-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "another-package", "version": "1.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-9": {Bundle: declcfg.Bundle{
Name: "bundle-9",
Package: "bar-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "bar-package", "version": "1.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"bar.io","kind":"Bar","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-10": {Bundle: declcfg.Bundle{
Name: "bundle-10",
Package: "bar-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "bar-package", "version": "2.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"bar.io","kind":"Bar","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},

// test-package-2 required package - no dependencies
"bundle-14": {Bundle: declcfg.Bundle{
Name: "bundle-14",
Package: "test-package-2",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "1.5.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-15": {Bundle: declcfg.Bundle{
Name: "bundle-15",
Package: "test-package-2",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "2.0.1"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-16": {Bundle: declcfg.Bundle{
Name: "bundle-16",
Package: "test-package-2",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "3.16.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},

// completely unrelated
"bundle-11": {Bundle: declcfg.Bundle{
Name: "bundle-11",
Package: "unrelated-package",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package", "version": "2.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1alpha1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-12": {Bundle: declcfg.Bundle{
Name: "bundle-12",
Package: "unrelated-package-2",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package-2", "version": "2.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1alpha1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
"bundle-13": {Bundle: declcfg.Bundle{
Name: "bundle-13",
Package: "unrelated-package-2",
Properties: []property.Property{
{Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package-2", "version": "3.0.0"}`)},
{Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1alpha1"}]`)},
}},
InChannels: []*catalogmetadata.Channel{&channel},
},
}

t.Run("convert bundle variables into global uniqueness constraint variables", func(t *testing.T) {
bundleVariables := []*olmvariables.BundleVariable{
olmvariables.NewBundleVariable(
bundleSet["bundle-2"],
[]*catalogmetadata.Bundle{
bundleSet["bundle-3"],
bundleSet["bundle-4"],
bundleSet["bundle-5"],
bundleSet["bundle-6"],
bundleSet["bundle-7"],
},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-1"],
[]*catalogmetadata.Bundle{
bundleSet["bundle-6"],
bundleSet["bundle-7"],
bundleSet["bundle-8"],
},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-3"],
[]*catalogmetadata.Bundle{},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-4"],
[]*catalogmetadata.Bundle{},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-5"],
[]*catalogmetadata.Bundle{},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-6"],
[]*catalogmetadata.Bundle{},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-7"],
[]*catalogmetadata.Bundle{
bundleSet["bundle-8"],
bundleSet["bundle-9"],
bundleSet["bundle-10"],
},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-8"],
[]*catalogmetadata.Bundle{},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-9"],
[]*catalogmetadata.Bundle{},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-10"],
[]*catalogmetadata.Bundle{},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-14"],
[]*catalogmetadata.Bundle{},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-15"],
[]*catalogmetadata.Bundle{},
),
olmvariables.NewBundleVariable(
bundleSet["bundle-16"],
[]*catalogmetadata.Bundle{},
),
}

variables, err := variablesources.MakeBundleUniquenessVariables(bundleVariables)
require.NoError(t, err)

expectedIDs := []string{
"test-package package uniqueness",
"some-package package uniqueness",
"some-other-package package uniqueness",
"another-package package uniqueness",
"bar-package package uniqueness",
"test-package-2 package uniqueness",
}
actualIDs := collectVariableIDs(variables)
assert.EqualValues(t, expectedIDs, actualIDs)
})
}

var channel = catalogmetadata.Channel{Channel: declcfg.Channel{Name: "stable"}}
var bundleSet = map[string]*catalogmetadata.Bundle{
// required package bundles
Expand Down
13 changes: 13 additions & 0 deletions internal/resolution/variablesources/variable_utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package variablesources_test

import (
"github.com/operator-framework/deppy/pkg/deppy"
)

func collectVariableIDs[T deppy.Variable](vars []T) []string {
ids := make([]string, 0, len(vars))
for _, v := range vars {
ids = append(ids, v.Identifier().String())
}
return ids
}

0 comments on commit 60ef728

Please sign in to comment.