diff --git a/internal/controllers/variable_source.go b/internal/controllers/variable_source.go index 75d647a9b..e361c96fb 100644 --- a/internal/controllers/variable_source.go +++ b/internal/controllers/variable_source.go @@ -22,7 +22,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" @@ -64,21 +63,35 @@ func (v *VariableSource) GetVariables(ctx context.Context) ([]deppy.Variable, er return nil, err } - // We are in process of getting rid of extra variable sources. - // See this for progress: https://github.com/operator-framework/operator-controller/issues/437 - vs := variablesources.NestedVariableSource{ - func(inputVariableSource input.VariableSource) (input.VariableSource, error) { - return variablesources.NewOperatorVariableSource(operatorList.Items, allBundles, inputVariableSource), nil - }, - func(inputVariableSource input.VariableSource) (input.VariableSource, error) { - return variablesources.NewBundleDeploymentVariableSource(operatorList.Items, bundleDeploymentList.Items, allBundles, inputVariableSource), nil - }, - func(inputVariableSource input.VariableSource) (input.VariableSource, error) { - return variablesources.NewBundlesAndDepsVariableSource(allBundles, inputVariableSource), nil - }, - func(inputVariableSource input.VariableSource) (input.VariableSource, error) { - return variablesources.NewCRDUniquenessConstraintsVariableSource(inputVariableSource), nil - }, + requiredPackages, err := variablesources.MakeRequiredPackageVariables(allBundles, operatorList.Items) + if err != nil { + return nil, err + } + + installedPackages, err := variablesources.MakeInstalledPackageVariables(allBundles, operatorList.Items, bundleDeploymentList.Items) + if err != nil { + return nil, err + } + + bundles, err := variablesources.MakeBundleVariables(allBundles, requiredPackages, installedPackages) + if err != nil { + return nil, err + } + + bundleUniqueness := variablesources.MakeBundleUniquenessVariables(bundles) + + result := []deppy.Variable{} + for _, v := range requiredPackages { + result = append(result, v) + } + for _, v := range installedPackages { + result = append(result, v) + } + for _, v := range bundles { + result = append(result, v) + } + for _, v := range bundleUniqueness { + result = append(result, v) } - return vs.GetVariables(ctx) + return result, nil } diff --git a/internal/controllers/variable_source_test.go b/internal/controllers/variable_source_test.go new file mode 100644 index 000000000..79b4d3e6d --- /dev/null +++ b/internal/controllers/variable_source_test.go @@ -0,0 +1,139 @@ +package controllers_test + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/rand" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/operator-framework/deppy/pkg/deppy" + "github.com/operator-framework/deppy/pkg/deppy/constraint" + "github.com/operator-framework/deppy/pkg/deppy/input" + "github.com/operator-framework/operator-registry/alpha/declcfg" + "github.com/operator-framework/operator-registry/alpha/property" + rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" + + operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" + "github.com/operator-framework/operator-controller/internal/controllers" + olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" + testutil "github.com/operator-framework/operator-controller/test/util" +) + +func TestVariableSource(t *testing.T) { + sch := runtime.NewScheme() + utilruntime.Must(operatorsv1alpha1.AddToScheme(sch)) + utilruntime.Must(rukpakv1alpha1.AddToScheme(sch)) + + stableChannel := catalogmetadata.Channel{Channel: declcfg.Channel{ + Name: "stable", + Entries: []declcfg.ChannelEntry{ + { + Name: "packageA.v2.0.0", + }, + }, + }} + bundleSet := map[string]*catalogmetadata.Bundle{ + "packageA.v2.0.0": { + Bundle: declcfg.Bundle{ + Name: "packageA.v2.0.0", + Package: "packageA", + Image: "foo.io/packageA/packageA:v2.0.0", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"packageA","version":"2.0.0"}`)}, + }, + }, + CatalogName: "fake-catalog", + InChannels: []*catalogmetadata.Channel{&stableChannel}, + }, + } + allBundles := make([]*catalogmetadata.Bundle, 0, len(bundleSet)) + for _, bundle := range bundleSet { + allBundles = append(allBundles, bundle) + } + + pkgName := "packageA" + opName := fmt.Sprintf("operator-test-%s", rand.String(8)) + operator := &operatorsv1alpha1.Operator{ + ObjectMeta: metav1.ObjectMeta{Name: opName}, + Spec: operatorsv1alpha1.OperatorSpec{ + PackageName: pkgName, + Channel: "stable", + Version: "2.0.0", + }, + } + + bd := &rukpakv1alpha1.BundleDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: opName, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: operatorsv1alpha1.GroupVersion.String(), + Kind: "Operator", + Name: operator.Name, + UID: operator.UID, + Controller: pointer.Bool(true), + BlockOwnerDeletion: pointer.Bool(true), + }, + }, + }, + Spec: rukpakv1alpha1.BundleDeploymentSpec{ + ProvisionerClassName: "core-rukpak-io-plain", + Template: &rukpakv1alpha1.BundleTemplate{ + Spec: rukpakv1alpha1.BundleSpec{ + ProvisionerClassName: "core-rukpak-io-registry", + Source: rukpakv1alpha1.BundleSource{ + Type: rukpakv1alpha1.SourceTypeImage, + Image: &rukpakv1alpha1.ImageSource{ + Ref: "foo.io/packageA/packageA:v2.0.0", + }, + }, + }, + }, + }, + } + fakeClient := fake.NewClientBuilder().WithScheme(sch).WithObjects(operator, bd).Build() + fakeCatalogClient := testutil.NewFakeCatalogClient(allBundles) + + vs := controllers.NewVariableSource(fakeClient, &fakeCatalogClient) + + vars, err := vs.GetVariables(context.Background()) + require.NoError(t, err) + + expectedVars := []deppy.Variable{ + olmvariables.NewRequiredPackageVariable("packageA", []*catalogmetadata.Bundle{ + bundleSet["packageA.v2.0.0"], + }), + olmvariables.NewInstalledPackageVariable("packageA", []*catalogmetadata.Bundle{ + bundleSet["packageA.v2.0.0"], + }), + olmvariables.NewBundleVariable(bundleSet["packageA.v2.0.0"], nil), + olmvariables.NewBundleUniquenessVariable( + "packageA package uniqueness", + deppy.Identifier("fake-catalog-packageA-packageA.v2.0.0"), + ), + } + gocmpopts := []cmp.Option{ + cmpopts.IgnoreUnexported(catalogmetadata.Bundle{}), + cmp.AllowUnexported( + olmvariables.RequiredPackageVariable{}, + olmvariables.InstalledPackageVariable{}, + olmvariables.BundleVariable{}, + olmvariables.BundleUniquenessVariable{}, + input.SimpleVariable{}, + constraint.DependencyConstraint{}, + constraint.AtMostConstraint{}, + ), + } + require.Empty(t, cmp.Diff(vars, expectedVars, gocmpopts...)) +} diff --git a/internal/resolution/variablesources/bundle.go b/internal/resolution/variablesources/bundle.go index 0ba3cb7ae..4a4851a21 100644 --- a/internal/resolution/variablesources/bundle.go +++ b/internal/resolution/variablesources/bundle.go @@ -1,12 +1,10 @@ package variablesources import ( - "context" "fmt" "sort" "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" @@ -15,53 +13,6 @@ import ( olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" ) -var _ input.VariableSource = &BundlesAndDepsVariableSource{} - -type BundlesAndDepsVariableSource struct { - allBundles []*catalogmetadata.Bundle - variableSources []input.VariableSource -} - -func NewBundlesAndDepsVariableSource(allBundles []*catalogmetadata.Bundle, inputVariableSources ...input.VariableSource) *BundlesAndDepsVariableSource { - return &BundlesAndDepsVariableSource{ - allBundles: allBundles, - variableSources: inputVariableSources, - } -} - -func (b *BundlesAndDepsVariableSource) GetVariables(ctx context.Context) ([]deppy.Variable, error) { - variables := []deppy.Variable{} - - for _, variableSource := range b.variableSources { - inputVariables, err := variableSource.GetVariables(ctx) - if err != nil { - return nil, err - } - variables = append(variables, inputVariables...) - } - - requiredPackages := []*olmvariables.RequiredPackageVariable{} - installedPackages := []*olmvariables.InstalledPackageVariable{} - for _, variable := range variables { - switch v := variable.(type) { - case *olmvariables.RequiredPackageVariable: - requiredPackages = append(requiredPackages, v) - case *olmvariables.InstalledPackageVariable: - installedPackages = append(installedPackages, v) - } - } - - bundles, err := MakeBundleVariables(b.allBundles, requiredPackages, installedPackages) - if err != nil { - return nil, err - } - - for _, v := range bundles { - variables = append(variables, v) - } - return variables, nil -} - func MakeBundleVariables( allBundles []*catalogmetadata.Bundle, requiredPackages []*olmvariables.RequiredPackageVariable, diff --git a/internal/resolution/variablesources/bundle_deployment.go b/internal/resolution/variablesources/bundle_deployment.go deleted file mode 100644 index c4ceafb18..000000000 --- a/internal/resolution/variablesources/bundle_deployment.go +++ /dev/null @@ -1,52 +0,0 @@ -package variablesources - -import ( - "context" - - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" - - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" - "github.com/operator-framework/operator-controller/internal/catalogmetadata" -) - -var _ input.VariableSource = &BundleDeploymentVariableSource{} - -type BundleDeploymentVariableSource struct { - operators []operatorsv1alpha1.Operator - bundleDeployments []rukpakv1alpha1.BundleDeployment - allBundles []*catalogmetadata.Bundle - inputVariableSource input.VariableSource -} - -func NewBundleDeploymentVariableSource(operators []operatorsv1alpha1.Operator, bundleDeployments []rukpakv1alpha1.BundleDeployment, allBundles []*catalogmetadata.Bundle, inputVariableSource input.VariableSource) *BundleDeploymentVariableSource { - return &BundleDeploymentVariableSource{ - operators: operators, - bundleDeployments: bundleDeployments, - allBundles: allBundles, - inputVariableSource: inputVariableSource, - } -} - -func (o *BundleDeploymentVariableSource) GetVariables(ctx context.Context) ([]deppy.Variable, error) { - variableSources := SliceVariableSource{} - if o.inputVariableSource != nil { - variableSources = append(variableSources, o.inputVariableSource) - } - - variables, err := variableSources.GetVariables(ctx) - if err != nil { - return nil, err - } - - installedPackages, err := MakeInstalledPackageVariables(o.allBundles, o.operators, o.bundleDeployments) - if err != nil { - return nil, err - } - - for _, v := range installedPackages { - variables = append(variables, v) - } - return variables, nil -} diff --git a/internal/resolution/variablesources/bundle_deployment_test.go b/internal/resolution/variablesources/bundle_deployment_test.go deleted file mode 100644 index e1d7249f0..000000000 --- a/internal/resolution/variablesources/bundle_deployment_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package variablesources_test - -import ( - "context" - "encoding/json" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/operator-framework/operator-registry/alpha/declcfg" - "github.com/operator-framework/operator-registry/alpha/property" - - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" - "github.com/operator-framework/operator-controller/internal/catalogmetadata" - olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" - "github.com/operator-framework/operator-controller/internal/resolution/variablesources" - - "github.com/operator-framework/deppy/pkg/deppy" - rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" -) - -var _ = Describe("BundleDeploymentVariableSource", func() { - var betaChannel catalogmetadata.Channel - var stableChannel catalogmetadata.Channel - var testBundleList []*catalogmetadata.Bundle - - BeforeEach(func() { - betaChannel = catalogmetadata.Channel{Channel: declcfg.Channel{ - Name: "beta", - Entries: []declcfg.ChannelEntry{ - { - Name: "operatorhub/prometheus/0.37.0", - Replaces: "operatorhub/prometheus/0.32.0", - }, - { - Name: "operatorhub/prometheus/0.47.0", - Replaces: "operatorhub/prometheus/0.37.0", - }, - }, - }} - - stableChannel = catalogmetadata.Channel{Channel: declcfg.Channel{ - Name: "beta", - Entries: []declcfg.ChannelEntry{ - { - Name: "operatorhub/packageA/2.0.0", - }, - }, - }} - - testBundleList = []*catalogmetadata.Bundle{ - {Bundle: declcfg.Bundle{ - Name: "operatorhub/prometheus/0.37.0", - Package: "prometheus", - Image: "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.37.0"}`)}, - {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"monitoring.coreos.com","kind":"Alertmanager","version":"v1"}, {"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1"}]`)}, - }, - }, InChannels: []*catalogmetadata.Channel{&betaChannel}}, - {Bundle: declcfg.Bundle{ - Name: "operatorhub/prometheus/0.47.0", - Package: "prometheus", - Image: "quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.47.0"}`)}, - {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"monitoring.coreos.com","kind":"Alertmanager","version":"v1"}, {"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1alpha1"}]`)}, - }, - }, InChannels: []*catalogmetadata.Channel{&betaChannel}}, - {Bundle: declcfg.Bundle{ - Name: "operatorhub/packageA/2.0.0", - Package: "packageA", - Image: "foo.io/packageA/packageA:v2.0.0", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"packageA","version":"2.0.0"}`)}, - {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, - }, - }, InChannels: []*catalogmetadata.Channel{&stableChannel}}, - } - }) - - It("should produce RequiredPackage variables", func() { - fakeOperator := fakeOperator("test-operator", "prometheus", operatorsv1alpha1.UpgradeConstraintPolicyEnforce) - operators := []operatorsv1alpha1.Operator{fakeOperator} - bundleDeployments := []rukpakv1alpha1.BundleDeployment{ - fakeBundleDeployment("prometheus", "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35", &fakeOperator), - } - - bdVariableSource := variablesources.NewBundleDeploymentVariableSource(operators, bundleDeployments, testBundleList, &MockRequiredPackageSource{}) - variables, err := bdVariableSource.GetVariables(context.Background()) - Expect(err).ToNot(HaveOccurred()) - - installedPackageVariable := filterVariables[*olmvariables.InstalledPackageVariable](variables) - Expect(installedPackageVariable).To(HaveLen(1)) - Expect(installedPackageVariable).To(WithTransform(func(bvars []*olmvariables.InstalledPackageVariable) map[deppy.Identifier]int { - out := map[deppy.Identifier]int{} - for _, variable := range bvars { - out[variable.Identifier()] = len(variable.Bundles()) - } - return out - }, Equal(map[deppy.Identifier]int{ - // Underlying `InstalledPackageVariableSource` returns current installed package - // as a possible upgrade edge - deppy.IdentifierFromString("installed package prometheus"): 2, - }))) - }) - It("should return an error if the bundleDeployment image doesn't match any operator resource", func() { - fakeOperator := fakeOperator("test-operator", "prometheus", operatorsv1alpha1.UpgradeConstraintPolicyEnforce) - operators := []operatorsv1alpha1.Operator{fakeOperator} - bundleDeployments := []rukpakv1alpha1.BundleDeployment{ - fakeBundleDeployment("prometheus", "quay.io/operatorhubio/prometheus@sha256:nonexistent", &fakeOperator), - } - - bdVariableSource := variablesources.NewBundleDeploymentVariableSource(operators, bundleDeployments, testBundleList, &MockRequiredPackageSource{}) - _, err := bdVariableSource.GetVariables(context.Background()) - Expect(err.Error()).To(Equal(`bundle with image "quay.io/operatorhubio/prometheus@sha256:nonexistent" for package "prometheus" not found in available catalogs but is currently installed via BundleDeployment "prometheus"`)) - }) -}) diff --git a/internal/resolution/variablesources/bundle_test.go b/internal/resolution/variablesources/bundle_test.go index bad8b7fdd..b4c55bdbd 100644 --- a/internal/resolution/variablesources/bundle_test.go +++ b/internal/resolution/variablesources/bundle_test.go @@ -1,16 +1,12 @@ package variablesources_test import ( - "context" "encoding/json" - "errors" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/operator-framework/deppy/pkg/deppy" + "github.com/operator-framework/deppy/pkg/deppy/constraint" "github.com/operator-framework/deppy/pkg/deppy/input" "github.com/operator-framework/operator-registry/alpha/declcfg" @@ -197,397 +193,3 @@ func TestMakeBundleVariables_NonExistentDepedencies(t *testing.T) { assert.ErrorContains(t, err, `could not determine dependencies for bundle "test-package.v1.0.0" from package "test-package" in catalog "fake-catalog"`) assert.Nil(t, bundles) } - -var _ = Describe("BundlesAndDepsVariableSource", func() { - var ( - bdvs *variablesources.BundlesAndDepsVariableSource - testBundleList []*catalogmetadata.Bundle - ) - - BeforeEach(func() { - channel := catalogmetadata.Channel{Channel: declcfg.Channel{Name: "stable"}} - testBundleList = []*catalogmetadata.Bundle{ - // required package bundles - { - CatalogName: "fake-catalog", - 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"}]`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - - { - CatalogName: "fake-catalog", - 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"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - - // dependencies - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-4", - Package: "some-package", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "1.0.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-5", - Package: "some-package", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "1.5.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-6", - Package: "some-package", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "2.0.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - 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.0.0"}`)}, - {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-8", - 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 - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-9", 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}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-10", - 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}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-11", - 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 - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-15", - Package: "test-package-2", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "1.5.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-16", - Package: "test-package-2", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "2.0.1"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-17", - Package: "test-package-2", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "3.16.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - - // completely unrelated - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-12", - Package: "unrelated-package", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package", "version": "2.0.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-13", - Package: "unrelated-package-2", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package-2", "version": "2.0.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-14", - Package: "unrelated-package-2", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package-2", "version": "3.0.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - } - bdvs = variablesources.NewBundlesAndDepsVariableSource( - testBundleList, - &MockRequiredPackageSource{ - ResultSet: []deppy.Variable{ - // must match data in testBundleList - olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{ - { - CatalogName: "fake-catalog", - 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"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - 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"}]`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - }), - }, - }, - &MockRequiredPackageSource{ - ResultSet: []deppy.Variable{ - // must match data in testBundleList - olmvariables.NewRequiredPackageVariable("test-package-2", []*catalogmetadata.Bundle{ - // test-package-2 required package - no dependencies - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-15", - Package: "test-package-2", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "1.5.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-16", - Package: "test-package-2", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "2.0.1"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - { - CatalogName: "fake-catalog", - Bundle: declcfg.Bundle{ - Name: "bundle-17", - Package: "test-package-2", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "3.16.0"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{&channel}, - }, - }), - }, - }, - ) - }) - - It("should return bundle variables with correct dependencies", func() { - variables, err := bdvs.GetVariables(context.TODO()) - Expect(err).NotTo(HaveOccurred()) - - var bundleVariables []*olmvariables.BundleVariable - for _, variable := range variables { - switch v := variable.(type) { - case *olmvariables.BundleVariable: - bundleVariables = append(bundleVariables, v) - } - } - // Note: When accounting for Required GVKs (currently not in use), we would expect additional - // dependencies (bundles 7, 8, 9, 10, 11) to appear here due to their GVKs being required by - // some of the packages. - Expect(bundleVariables).To(WithTransform(CollectBundleVariableIDs, Equal([]string{ - "fake-catalog-test-package-bundle-2", - "fake-catalog-test-package-bundle-1", - "fake-catalog-test-package-2-bundle-15", - "fake-catalog-test-package-2-bundle-16", - "fake-catalog-test-package-2-bundle-17", - "fake-catalog-some-package-bundle-5", - "fake-catalog-some-package-bundle-4", - }))) - - // check dependencies for one of the bundles - bundle2 := VariableWithName("bundle-2")(bundleVariables) - // Note: As above, bundle-2 has GVK requirements satisfied by bundles 7, 8, and 9, but they - // will not appear in this list as we are not currently taking Required GVKs into account - Expect(bundle2.Dependencies()).To(HaveLen(2)) - Expect(bundle2.Dependencies()[0].Name).To(Equal("bundle-5")) - Expect(bundle2.Dependencies()[1].Name).To(Equal("bundle-4")) - }) - - It("should return error if dependencies not found", func() { - bdvs = variablesources.NewBundlesAndDepsVariableSource( - []*catalogmetadata.Bundle{}, - &MockRequiredPackageSource{ - ResultSet: []deppy.Variable{ - olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{ - { - CatalogName: "fake-catalog", - 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"}`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{{Channel: declcfg.Channel{Name: "stable"}}}, - }, - { - CatalogName: "fake-catalog", - 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"}]`)}, - }, - }, - InChannels: []*catalogmetadata.Channel{{Channel: declcfg.Channel{Name: "stable"}}}, - }, - }), - }, - }, - ) - _, err := bdvs.GetVariables(context.TODO()) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`could not determine dependencies for bundle "bundle-2" from package "test-package" in catalog "fake-catalog": no bundles found matching required package "some-package" in range ">=1.0.0 <2.0.0"`)) - }) - - It("should return error if an inner variable source returns an error", func() { - bdvs = variablesources.NewBundlesAndDepsVariableSource( - testBundleList, - &MockRequiredPackageSource{Error: errors.New("fake error")}, - ) - _, err := bdvs.GetVariables(context.TODO()) - Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError("fake error")) - }) -}) - -type MockRequiredPackageSource struct { - ResultSet []deppy.Variable - Error error -} - -func (m *MockRequiredPackageSource) GetVariables(_ context.Context) ([]deppy.Variable, error) { - return m.ResultSet, m.Error -} - -func VariableWithName(name string) func(vars []*olmvariables.BundleVariable) *olmvariables.BundleVariable { - return func(vars []*olmvariables.BundleVariable) *olmvariables.BundleVariable { - for i := 0; i < len(vars); i++ { - if vars[i].Bundle().Name == name { - return vars[i] - } - } - return nil - } -} - -func CollectBundleVariableIDs(vars []*olmvariables.BundleVariable) []string { - ids := make([]string, 0, len(vars)) - for _, v := range vars { - ids = append(ids, v.Identifier().String()) - } - return ids -} diff --git a/internal/resolution/variablesources/bundle_uniqueness.go b/internal/resolution/variablesources/bundle_uniqueness.go index 1247c0c2a..cb4546e2c 100644 --- a/internal/resolution/variablesources/bundle_uniqueness.go +++ b/internal/resolution/variablesources/bundle_uniqueness.go @@ -1,13 +1,11 @@ package variablesources import ( - "context" "fmt" "k8s.io/apimachinery/pkg/util/sets" "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" @@ -51,45 +49,3 @@ func MakeBundleUniquenessVariables(bundleVariables []*olmvariables.BundleVariabl return result } - -// CRDUniquenessConstraintsVariableSource produces variables that constraint the solution to -// 1. at most 1 bundle per package -// 2. at most 1 bundle per gvk (provided by the bundle) -// these variables guarantee that no two operators provide the same gvk and no two version of -// the same operator are running at the same time. -// This variable source does not itself reach out to catalog metadata. It produces its variables -// by searching for BundleVariables that are produced by its 'inputVariableSource' and working out -// which bundles correspond to which package and which gvks are provided by which bundle -type CRDUniquenessConstraintsVariableSource struct { - inputVariableSource input.VariableSource -} - -// NewCRDUniquenessConstraintsVariableSource creates a new instance of the CRDUniquenessConstraintsVariableSource. -// its purpose if to provide variables with constraints that restrict the solutions to bundle sets where -// no two bundles come from the same package and not two bundles provide the same gvk -func NewCRDUniquenessConstraintsVariableSource(inputVariableSource input.VariableSource) *CRDUniquenessConstraintsVariableSource { - return &CRDUniquenessConstraintsVariableSource{ - inputVariableSource: inputVariableSource, - } -} - -func (g *CRDUniquenessConstraintsVariableSource) GetVariables(ctx context.Context) ([]deppy.Variable, error) { - variables, err := g.inputVariableSource.GetVariables(ctx) - if err != nil { - return nil, err - } - - bundleVariables := []*olmvariables.BundleVariable{} - for _, variable := range variables { - switch v := variable.(type) { - case *olmvariables.BundleVariable: - bundleVariables = append(bundleVariables, v) - } - } - - bundleUniqueness := MakeBundleUniquenessVariables(bundleVariables) - for _, v := range bundleUniqueness { - variables = append(variables, v) - } - return variables, nil -} diff --git a/internal/resolution/variablesources/bundle_uniqueness_test.go b/internal/resolution/variablesources/bundle_uniqueness_test.go index 6b63f50b1..6404bbcd3 100644 --- a/internal/resolution/variablesources/bundle_uniqueness_test.go +++ b/internal/resolution/variablesources/bundle_uniqueness_test.go @@ -1,14 +1,10 @@ package variablesources_test import ( - "context" "encoding/json" - "fmt" "testing" "github.com/google/go-cmp/cmp" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" "github.com/stretchr/testify/require" "github.com/operator-framework/deppy/pkg/deppy" @@ -113,309 +109,3 @@ func TestMakeBundleUniquenessVariables(t *testing.T) { } require.Empty(t, cmp.Diff(variables, expectedVariables, cmp.AllowUnexported(input.SimpleVariable{}, constraint.AtMostConstraint{}))) } - -var channel = catalogmetadata.Channel{Channel: declcfg.Channel{Name: "stable"}} -var 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}, - }, -} - -var _ = Describe("CRDUniquenessConstraintsVariableSource", func() { - var ( - inputVariableSource *MockInputVariableSource - crdConstraintVariableSource *variablesources.CRDUniquenessConstraintsVariableSource - ctx context.Context - ) - - BeforeEach(func() { - inputVariableSource = &MockInputVariableSource{} - crdConstraintVariableSource = variablesources.NewCRDUniquenessConstraintsVariableSource(inputVariableSource) - ctx = context.Background() - }) - - It("should get variables from the input variable source and create global constraint variables", func() { - inputVariableSource.ResultSet = []deppy.Variable{ - olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{ - bundleSet["bundle-2"], - bundleSet["bundle-1"], - }), - olmvariables.NewRequiredPackageVariable("test-package-2", []*catalogmetadata.Bundle{ - bundleSet["bundle-14"], - bundleSet["bundle-15"], - bundleSet["bundle-16"], - }), - 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 := crdConstraintVariableSource.GetVariables(ctx) - Expect(err).ToNot(HaveOccurred()) - // Note: When accounting for GVK Uniqueness (which we are currently not doing), we - // would expect to have 26 variables from the 5 unique GVKs (Bar, Bit, Buz, Fiz, Foo). - Expect(variables).To(HaveLen(21)) - var crdConstraintVariables []*olmvariables.BundleUniquenessVariable - for _, variable := range variables { - switch v := variable.(type) { - case *olmvariables.BundleUniquenessVariable: - crdConstraintVariables = append(crdConstraintVariables, v) - } - } - // Note: As above, the 5 GVKs would appear here as GVK uniqueness constraints - // if GVK Uniqueness were being accounted for. - Expect(crdConstraintVariables).To(WithTransform(CollectGlobalConstraintVariableIDs, Equal([]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", - }))) - }) - - It("should return an error if input variable source returns an error", func() { - inputVariableSource = &MockInputVariableSource{Err: fmt.Errorf("error getting variables")} - crdConstraintVariableSource = variablesources.NewCRDUniquenessConstraintsVariableSource(inputVariableSource) - _, err := crdConstraintVariableSource.GetVariables(ctx) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("error getting variables")) - }) -}) - -type MockInputVariableSource struct { - ResultSet []deppy.Variable - Err error -} - -func (m *MockInputVariableSource) GetVariables(_ context.Context) ([]deppy.Variable, error) { - if m.Err != nil { - return nil, m.Err - } - return m.ResultSet, nil -} - -func CollectGlobalConstraintVariableIDs(vars []*olmvariables.BundleUniquenessVariable) []string { - ids := make([]string, 0, len(vars)) - for _, v := range vars { - ids = append(ids, v.Identifier().String()) - } - return ids -} diff --git a/internal/resolution/variablesources/composite.go b/internal/resolution/variablesources/composite.go deleted file mode 100644 index d0e3a20b9..000000000 --- a/internal/resolution/variablesources/composite.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright 2023. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package variablesources - -import ( - "context" - "errors" - - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" -) - -var _ input.VariableSource = &SliceVariableSource{} -var _ input.VariableSource = &NestedVariableSource{} - -type NestedVariableSource []func(inputVariableSource input.VariableSource) (input.VariableSource, error) - -func (s NestedVariableSource) GetVariables(ctx context.Context) ([]deppy.Variable, error) { - if len(s) == 0 { - return nil, errors.New("empty nested variable sources") - } - - var variableSource input.VariableSource - var err error - for _, constructor := range s { - variableSource, err = constructor(variableSource) - if err != nil { - return nil, err - } - } - - return variableSource.GetVariables(ctx) -} - -type SliceVariableSource []input.VariableSource - -func (s SliceVariableSource) GetVariables(ctx context.Context) ([]deppy.Variable, error) { - var variables []deppy.Variable - for _, variableSource := range s { - inputVariables, err := variableSource.GetVariables(ctx) - if err != nil { - return nil, err - } - variables = append(variables, inputVariables...) - } - - return variables, nil -} diff --git a/internal/resolution/variablesources/composite_test.go b/internal/resolution/variablesources/composite_test.go deleted file mode 100644 index bfbf859e8..000000000 --- a/internal/resolution/variablesources/composite_test.go +++ /dev/null @@ -1,172 +0,0 @@ -/* -Copyright 2023. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package variablesources_test - -import ( - "context" - "errors" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" - - "github.com/operator-framework/operator-controller/internal/resolution/variablesources" -) - -func TestNestedVariableSource(t *testing.T) { - for _, tt := range []struct { - name string - varSources []*mockVariableSource - - wantVariables []deppy.Variable - wantErr string - }{ - { - name: "multiple nested sources", - varSources: []*mockVariableSource{ - {fakeVariables: []deppy.Variable{mockVariable("fake-var-1"), mockVariable("fake-var-2")}}, - {fakeVariables: []deppy.Variable{mockVariable("fake-var-3")}}, - }, - wantVariables: []deppy.Variable{mockVariable("fake-var-1"), mockVariable("fake-var-2"), mockVariable("fake-var-3")}, - }, - { - name: "error when no nested sources provided", - wantErr: "empty nested variable sources", - }, - } { - t.Run(tt.name, func(t *testing.T) { - ctx := context.Background() - - nestedSource := variablesources.NestedVariableSource{} - for i := range tt.varSources { - i := i // Same reason as https://go.dev/doc/faq#closures_and_goroutines - nestedSource = append(nestedSource, func(inputVariableSource input.VariableSource) (input.VariableSource, error) { - if i == 0 { - assert.Nil(t, inputVariableSource) - } else { - assert.Equal(t, tt.varSources[i-1], inputVariableSource) - - tt.varSources[i].inputVariableSource = inputVariableSource - } - - return tt.varSources[i], nil - }) - } - - variables, err := nestedSource.GetVariables(ctx) - if tt.wantErr != "" { - assert.EqualError(t, err, tt.wantErr) - } else { - assert.NoError(t, err) - } - assert.Equal(t, tt.wantVariables, variables) - }) - } - - t.Run("error from a nested constructor", func(t *testing.T) { - ctx := context.Background() - - nestedSource := variablesources.NestedVariableSource{ - func(inputVariableSource input.VariableSource) (input.VariableSource, error) { - return nil, errors.New("fake error from a constructor") - }, - } - - variables, err := nestedSource.GetVariables(ctx) - assert.EqualError(t, err, "fake error from a constructor") - assert.Nil(t, variables) - }) -} - -func TestSliceVariableSource(t *testing.T) { - for _, tt := range []struct { - name string - varSources []input.VariableSource - - wantVariables []deppy.Variable - wantErr string - }{ - { - name: "multiple sources in the slice", - varSources: []input.VariableSource{ - &mockVariableSource{fakeVariables: []deppy.Variable{mockVariable("fake-var-1"), mockVariable("fake-var-2")}}, - &mockVariableSource{fakeVariables: []deppy.Variable{mockVariable("fake-var-3")}}, - }, - wantVariables: []deppy.Variable{mockVariable("fake-var-1"), mockVariable("fake-var-2"), mockVariable("fake-var-3")}, - }, - { - name: "error from GetVariables", - varSources: []input.VariableSource{ - &mockVariableSource{fakeVariables: []deppy.Variable{mockVariable("fake-var-1"), mockVariable("fake-var-2")}}, - &mockVariableSource{fakeError: errors.New("fake error from GetVariables")}, - }, - wantErr: "fake error from GetVariables", - }, - } { - t.Run(tt.name, func(t *testing.T) { - ctx := context.Background() - - sliceSource := variablesources.SliceVariableSource(tt.varSources) - variables, err := sliceSource.GetVariables(ctx) - if tt.wantErr != "" { - assert.EqualError(t, err, tt.wantErr) - } else { - assert.NoError(t, err) - } - assert.Equal(t, tt.wantVariables, variables) - }) - } -} - -var _ input.VariableSource = &mockVariableSource{} - -type mockVariableSource struct { - inputVariableSource input.VariableSource - fakeVariables []deppy.Variable - fakeError error -} - -func (m *mockVariableSource) GetVariables(ctx context.Context) ([]deppy.Variable, error) { - if m.fakeError != nil { - return nil, m.fakeError - } - - if m.inputVariableSource == nil { - return m.fakeVariables, nil - } - - nestedVars, err := m.inputVariableSource.GetVariables(ctx) - if err != nil { - return nil, err - } - - return append(nestedVars, m.fakeVariables...), nil -} - -var _ deppy.Variable = mockVariable("") - -type mockVariable string - -func (m mockVariable) Identifier() deppy.Identifier { - return deppy.IdentifierFromString(string(m)) -} - -func (m mockVariable) Constraints() []deppy.Constraint { - return nil -} diff --git a/internal/resolution/variablesources/operator.go b/internal/resolution/variablesources/operator.go deleted file mode 100644 index 9e3f8ed42..000000000 --- a/internal/resolution/variablesources/operator.go +++ /dev/null @@ -1,49 +0,0 @@ -package variablesources - -import ( - "context" - - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" - "github.com/operator-framework/operator-controller/internal/catalogmetadata" - - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" -) - -var _ input.VariableSource = &OperatorVariableSource{} - -type OperatorVariableSource struct { - operators []operatorsv1alpha1.Operator - allBundles []*catalogmetadata.Bundle - inputVariableSource input.VariableSource -} - -func NewOperatorVariableSource(operators []operatorsv1alpha1.Operator, allBundles []*catalogmetadata.Bundle, inputVariableSource input.VariableSource) *OperatorVariableSource { - return &OperatorVariableSource{ - operators: operators, - allBundles: allBundles, - inputVariableSource: inputVariableSource, - } -} - -func (o *OperatorVariableSource) GetVariables(ctx context.Context) ([]deppy.Variable, error) { - variableSources := SliceVariableSource{} - if o.inputVariableSource != nil { - variableSources = append(variableSources, o.inputVariableSource) - } - - requiredPackages, err := MakeRequiredPackageVariables(o.allBundles, o.operators) - if err != nil { - return nil, err - } - - variables, err := variableSources.GetVariables(ctx) - if err != nil { - return nil, err - } - - for _, v := range requiredPackages { - variables = append(variables, v) - } - return variables, nil -} diff --git a/internal/resolution/variablesources/operator_test.go b/internal/resolution/variablesources/operator_test.go deleted file mode 100644 index 1e8d2fbe4..000000000 --- a/internal/resolution/variablesources/operator_test.go +++ /dev/null @@ -1,147 +0,0 @@ -package variablesources_test - -import ( - "context" - "encoding/json" - - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/operator-registry/alpha/declcfg" - "github.com/operator-framework/operator-registry/alpha/property" - - . "github.com/onsi/ginkgo/v2" - - . "github.com/onsi/gomega" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" - - "github.com/operator-framework/operator-controller/internal/catalogmetadata" - olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" - "github.com/operator-framework/operator-controller/internal/resolution/variablesources" -) - -func FakeClient(objects ...client.Object) client.Client { - scheme := runtime.NewScheme() - utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme)) - return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build() -} - -func operator(name string) operatorsv1alpha1.Operator { - return operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Spec: operatorsv1alpha1.OperatorSpec{ - PackageName: name, - }, - } -} - -var _ = Describe("OperatorVariableSource", func() { - var betaChannel catalogmetadata.Channel - var stableChannel catalogmetadata.Channel - var testBundleList []*catalogmetadata.Bundle - - BeforeEach(func() { - betaChannel = catalogmetadata.Channel{ - Channel: declcfg.Channel{ - Name: "beta", - Entries: []declcfg.ChannelEntry{ - { - Name: "operatorhub/prometheus/0.37.0", - Replaces: "operatorhub/prometheus/0.32.0", - }, - { - Name: "operatorhub/prometheus/0.47.0", - Replaces: "operatorhub/prometheus/0.37.0", - }, - }, - }, - } - - stableChannel = catalogmetadata.Channel{ - Channel: declcfg.Channel{ - Name: "stable", - Entries: []declcfg.ChannelEntry{ - { - Name: "operatorhub/packageA/2.0.0", - }, - }, - }, - } - - testBundleList = []*catalogmetadata.Bundle{ - {Bundle: declcfg.Bundle{ - Name: "operatorhub/prometheus/0.37.0", - Package: "prometheus", - Image: "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.37.0"}`)}, - {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"monitoring.coreos.com","kind":"Alertmanager","version":"v1"}, {"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1"}]`)}, - }}, - InChannels: []*catalogmetadata.Channel{&betaChannel}, - }, - {Bundle: declcfg.Bundle{ - Name: "operatorhub/prometheus/0.47.0", - Package: "prometheus", - Image: "quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.47.0"}`)}, - {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"monitoring.coreos.com","kind":"Alertmanager","version":"v1"}, {"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1alpha1"}]`)}, - }}, - InChannels: []*catalogmetadata.Channel{&betaChannel}, - }, - {Bundle: declcfg.Bundle{ - Name: "operatorhub/packageA/2.0.0", - Package: "packageA", - Image: "foo.io/packageA/packageA:v2.0.0", - Properties: []property.Property{ - {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"packageA","version":"2.0.0"}`)}, - {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, - }}, - InChannels: []*catalogmetadata.Channel{&stableChannel}, - }, - } - - }) - - It("should produce RequiredPackage variables", func() { - operators := []operatorsv1alpha1.Operator{ - operator("prometheus"), - operator("packageA"), - } - opVariableSource := variablesources.NewOperatorVariableSource(operators, testBundleList, &MockRequiredPackageSource{}) - variables, err := opVariableSource.GetVariables(context.Background()) - Expect(err).ToNot(HaveOccurred()) - - packageRequiredVariables := filterVariables[*olmvariables.RequiredPackageVariable](variables) - Expect(packageRequiredVariables).To(HaveLen(2)) - Expect(packageRequiredVariables).To(WithTransform(func(bvars []*olmvariables.RequiredPackageVariable) map[deppy.Identifier]int { - out := map[deppy.Identifier]int{} - for _, variable := range bvars { - out[variable.Identifier()] = len(variable.Bundles()) - } - return out - }, Equal(map[deppy.Identifier]int{ - deppy.IdentifierFromString("required package prometheus"): 2, - deppy.IdentifierFromString("required package packageA"): 1, - }))) - }) -}) - -func filterVariables[D deppy.Variable](variables []deppy.Variable) []D { - var out []D - for _, variable := range variables { - switch v := variable.(type) { - case D: - out = append(out, v) - } - } - return out -} diff --git a/internal/resolution/variablesources/variablesources_test.go b/internal/resolution/variablesources/variablesources_test.go deleted file mode 100644 index 7bb8d97b8..000000000 --- a/internal/resolution/variablesources/variablesources_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package variablesources_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestVariableSources(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Variable Sources Suite") -}