diff --git a/.gitignore b/.gitignore index 2d49c9a2d..81ec391c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -vendor # Binaries for programs and plugins *.exe diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 5e5fc54fa..6caa5b6b1 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -5,13 +5,6 @@ metadata: creationTimestamp: null name: manager-role rules: -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - apiGroups: - core.rukpak.io resources: @@ -23,14 +16,6 @@ rules: - patch - update - watch -- apiGroups: - - operators.coreos.com - resources: - - catalogsources - verbs: - - get - - list - - watch - apiGroups: - operators.operatorframework.io resources: diff --git a/controllers/catalogsource_controller.go b/controllers/catalogsource_controller.go deleted file mode 100644 index 874e6169a..000000000 --- a/controllers/catalogsource_controller.go +++ /dev/null @@ -1,203 +0,0 @@ -package controllers - -import ( - "context" - "fmt" - "reflect" - "sync" - "time" - - "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/operator-framework/operator-controller/internal/resolution/entity_sources/catalogsource" -) - -const ( - defaultCatalogSourceSyncInterval = 5 * time.Minute - defaultRegistryGRPCConnectionTimeout = 10 * time.Second - - eventTypeNormal = "Normal" - eventTypeWarning = "Warning" - - eventReasonCacheUpdated = "BundleCacheUpdated" - eventReasonCacheUpdateFailed = "BundleCacheUpdateFailed" -) - -type CatalogSourceReconcilerOption func(reconciler *CatalogSourceReconciler) - -func WithRegistryClient(registry catalogsource.RegistryClient) CatalogSourceReconcilerOption { - return func(reconciler *CatalogSourceReconciler) { - reconciler.registry = registry - } -} - -func WithUnmanagedCatalogSourceSyncInterval(interval time.Duration) CatalogSourceReconcilerOption { - return func(reconciler *CatalogSourceReconciler) { - reconciler.unmanagedCatalogSourceSyncInterval = interval - } -} - -// applyDefaults applies default values to empty CatalogSourceReconciler fields _after_ options have been applied -func applyDefaults() CatalogSourceReconcilerOption { - return func(reconciler *CatalogSourceReconciler) { - if reconciler.registry == nil { - reconciler.registry = catalogsource.NewRegistryGRPCClient(defaultRegistryGRPCConnectionTimeout) - } - if reconciler.unmanagedCatalogSourceSyncInterval == 0 { - reconciler.unmanagedCatalogSourceSyncInterval = defaultCatalogSourceSyncInterval - } - } -} - -type CatalogSourceReconciler struct { - sync.RWMutex - client.Client - scheme *runtime.Scheme - registry catalogsource.RegistryClient - recorder record.EventRecorder - unmanagedCatalogSourceSyncInterval time.Duration - cache map[string]map[deppy.Identifier]*input.Entity -} - -func NewCatalogSourceReconciler(client client.Client, scheme *runtime.Scheme, recorder record.EventRecorder, options ...CatalogSourceReconcilerOption) *CatalogSourceReconciler { - reconciler := &CatalogSourceReconciler{ - RWMutex: sync.RWMutex{}, - Client: client, - scheme: scheme, - recorder: recorder, - unmanagedCatalogSourceSyncInterval: 0, - cache: map[string]map[deppy.Identifier]*input.Entity{}, - } - // apply options - options = append(options, applyDefaults()) - for _, option := range options { - option(reconciler) - } - - return reconciler -} - -// +kubebuilder:rbac:groups=operators.coreos.com,resources=catalogsources,verbs=get;list;watch -// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch - -func (r *CatalogSourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - l := log.FromContext(ctx).WithName("catalogsource-controller") - l.V(1).Info("starting") - defer l.V(1).Info("ending") - - var catalogSource = &v1alpha1.CatalogSource{} - if err := r.Client.Get(ctx, req.NamespacedName, catalogSource); err != nil { - if errors.IsNotFound(err) { - r.dropSource(req.String()) - } - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - entities, err := r.registry.ListEntities(ctx, catalogSource) - // TODO: invalidate stale cache for failed updates - if err != nil { - r.recorder.Event(catalogSource, eventTypeWarning, eventReasonCacheUpdateFailed, fmt.Sprintf("Failed to update bundle cache from %s/%s: %v", catalogSource.GetNamespace(), catalogSource.GetName(), err)) - return ctrl.Result{Requeue: !isManagedCatalogSource(*catalogSource)}, err - } - if updated := r.updateCache(req.String(), entities); updated { - r.recorder.Event(catalogSource, eventTypeNormal, eventReasonCacheUpdated, fmt.Sprintf("Successfully updated bundle cache from %s/%s", catalogSource.GetNamespace(), catalogSource.GetName())) - } - - if isManagedCatalogSource(*catalogSource) { - return ctrl.Result{}, nil - } - return ctrl.Result{RequeueAfter: r.unmanagedCatalogSourceSyncInterval}, nil -} - -// SetupWithManager sets up the controller with the Manager. -func (r *CatalogSourceReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1.CatalogSource{}). - Complete(r) -} - -// TODO: find better way to identify catalogSources unmanaged by olm -func isManagedCatalogSource(catalogSource v1alpha1.CatalogSource) bool { - return len(catalogSource.Spec.Address) == 0 -} - -func (r *CatalogSourceReconciler) updateCache(sourceID string, entities []*input.Entity) bool { - newSourceCache := make(map[deppy.Identifier]*input.Entity) - for _, entity := range entities { - newSourceCache[entity.Identifier()] = entity - } - if _, ok := r.cache[sourceID]; ok && reflect.DeepEqual(r.cache[sourceID], newSourceCache) { - return false - } - r.RWMutex.Lock() - defer r.RWMutex.Unlock() - r.cache[sourceID] = newSourceCache - // return whether cache had updates - return true -} - -func (r *CatalogSourceReconciler) dropSource(sourceID string) { - r.RWMutex.Lock() - defer r.RWMutex.Unlock() - delete(r.cache, sourceID) -} - -func (r *CatalogSourceReconciler) Get(ctx context.Context, id deppy.Identifier) *input.Entity { - r.RWMutex.RLock() - defer r.RWMutex.RUnlock() - // don't count on deppy ID to reflect its catalogsource - for _, source := range r.cache { - if entity, ok := source[id]; ok { - return entity - } - } - return nil -} - -func (r *CatalogSourceReconciler) Filter(ctx context.Context, filter input.Predicate) (input.EntityList, error) { - resultSet := input.EntityList{} - if err := r.Iterate(ctx, func(entity *input.Entity) error { - if filter(entity) { - resultSet = append(resultSet, *entity) - } - return nil - }); err != nil { - return nil, err - } - return resultSet, nil -} - -func (r *CatalogSourceReconciler) GroupBy(ctx context.Context, fn input.GroupByFunction) (input.EntityListMap, error) { - resultSet := input.EntityListMap{} - if err := r.Iterate(ctx, func(entity *input.Entity) error { - keys := fn(entity) - for _, key := range keys { - resultSet[key] = append(resultSet[key], *entity) - } - return nil - }); err != nil { - return nil, err - } - return resultSet, nil -} - -func (r *CatalogSourceReconciler) Iterate(ctx context.Context, fn input.IteratorFunction) error { - r.RWMutex.RLock() - defer r.RWMutex.RUnlock() - for _, source := range r.cache { - for _, entity := range source { - if err := fn(entity); err != nil { - return err - } - } - } - return nil -} diff --git a/controllers/catalogsource_controller_test.go b/controllers/catalogsource_controller_test.go deleted file mode 100644 index 38b02f227..000000000 --- a/controllers/catalogsource_controller_test.go +++ /dev/null @@ -1,364 +0,0 @@ -package controllers_test - -import ( - "context" - "fmt" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/rand" - "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" - - catsrccontroller "github.com/operator-framework/operator-controller/controllers" - "github.com/operator-framework/operator-controller/internal/resolution/entity_sources/catalogsource" -) - -var _ catalogsource.RegistryClient = &fakeRegistryClient{} - -const ( - unmanagedCatalogSourceSyncInterface = 1 * time.Second -) - -type catalogContents struct { - Entities []*input.Entity - err error -} - -type fakeRegistryClient struct { - catalogSource map[string]catalogContents -} - -func newFakeRegistryClient() *fakeRegistryClient { - return &fakeRegistryClient{ - catalogSource: map[string]catalogContents{}, - } -} - -func (r *fakeRegistryClient) setEntitiesForSource(catalogSourceID string, entities ...*input.Entity) { - r.catalogSource[catalogSourceID] = catalogContents{ - Entities: entities, - } -} - -func (r *fakeRegistryClient) setErrorForSource(catalogSourceID string, err error) { - r.catalogSource[catalogSourceID] = catalogContents{ - err: err, - } -} - -func (r *fakeRegistryClient) ListEntities(ctx context.Context, catsrc *v1alpha1.CatalogSource) ([]*input.Entity, error) { - catalogSourceKey := types.NamespacedName{Namespace: catsrc.Namespace, Name: catsrc.Name}.String() - if src, ok := r.catalogSource[catalogSourceKey]; ok { - return src.Entities, src.err - } - return []*input.Entity{}, nil -} - -var _ = Describe("CatalogSource Controller Test", func() { - var ( - ctx context.Context - reconciler *catsrccontroller.CatalogSourceReconciler - fakeRecorder *record.FakeRecorder - fakeRegistry *fakeRegistryClient - ) - BeforeEach(func() { - ctx = context.Background() - fakeRecorder = record.NewFakeRecorder(5) - fakeRegistry = newFakeRegistryClient() - reconciler = catsrccontroller.NewCatalogSourceReconciler( - cl, - sch, - fakeRecorder, - catsrccontroller.WithRegistryClient(fakeRegistry), - catsrccontroller.WithUnmanagedCatalogSourceSyncInterval(unmanagedCatalogSourceSyncInterface), - ) - }) - Describe("cache managements", func() { - When("the catalog source does not exist", func() { - It("returns no error", func() { - res, err := reconciler.Reconcile(context.Background(), ctrl.Request{NamespacedName: types.NamespacedName{Name: "non-existent", Namespace: "some-namespace"}}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).NotTo(HaveOccurred()) - }) - }) - When("the catalog source exists", func() { - var ( - catalogSource *v1alpha1.CatalogSource - catSrcKey types.NamespacedName - catalogSourceName string - namespace string - ) - When("the catalog source is managed by OLM", func() { - BeforeEach(func() { - catalogSourceName = fmt.Sprintf("catalogsource-test-%s", rand.String(8)) - namespace = fmt.Sprintf("test-namespace-%s", rand.String(8)) - Expect(cl.Create(ctx, &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespace, - }, - })).To(Succeed()) - catSrcKey = types.NamespacedName{Name: catalogSourceName, Namespace: namespace} - - By("initializing cluster state") - catalogSource = &v1alpha1.CatalogSource{ - ObjectMeta: metav1.ObjectMeta{Name: catalogSourceName, Namespace: namespace}, - } - err := cl.Create(ctx, catalogSource) - Expect(err).NotTo(HaveOccurred()) - }) - It("manages the cache", func() { - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catSrcKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).ToNot(HaveOccurred()) - - By("checking the cache is empty") - var entities []*input.Entity - err = reconciler.Iterate(ctx, func(entity *input.Entity) error { - entities = append(entities, entity) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - Expect(entities).To(BeEmpty()) - - By("updating the catalog source contents") - registryEntities := []*input.Entity{ - input.NewEntity(deppy.Identifier(fmt.Sprintf("%s/%s/pkg1/chan1/0.1.0", catalogSourceName, namespace)), map[string]string{}), - } - fakeRegistry.setEntitiesForSource(catSrcKey.String(), registryEntities...) - - By("re-running reconcile") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catSrcKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).ToNot(HaveOccurred()) - - By("checking the cache is populated") - entities = nil - err = reconciler.Iterate(ctx, func(entity *input.Entity) error { - entities = append(entities, entity) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - Expect(entities).To(Equal(registryEntities)) - - By("checking an event has been emitted") - event := <-fakeRecorder.Events - Expect(event).To(Equal(fmt.Sprintf("Normal BundleCacheUpdated Successfully updated bundle cache from %s/%s", namespace, catalogSourceName))) - - By("deleting the catalog source") - Expect(cl.Delete(ctx, catalogSource)).To(Succeed()) - - By("re-running reconcile") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catSrcKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).ToNot(HaveOccurred()) - - By("checking the cache is empty again") - entities = nil - err = reconciler.Iterate(ctx, func(entity *input.Entity) error { - entities = append(entities, entity) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - Expect(entities).To(BeEmpty()) - }) - - It("surfaces list bundles errors", func() { - By("creating a fake registry that errors out") - fakeRegistry.setErrorForSource(catSrcKey.String(), fmt.Errorf("something bad happened")) - - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catSrcKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).To(MatchError("something bad happened")) - - By("checking an event has been emitted") - event := <-fakeRecorder.Events - Expect(event).To(Equal(fmt.Sprintf("Warning BundleCacheUpdateFailed Failed to update bundle cache from %s/%s: something bad happened", namespace, catalogSourceName))) - }) - }) - - When("the catalog source is not managed by OLM", func() { - BeforeEach(func() { - catalogSourceName = fmt.Sprintf("catalogsource-test-%s", rand.String(8)) - namespace = fmt.Sprintf("test-namespace-%s", rand.String(8)) - Expect(cl.Create(ctx, &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespace, - }, - })).To(Succeed()) - catSrcKey = types.NamespacedName{Name: catalogSourceName, Namespace: namespace} - - By("initializing cluster state") - catalogSource = &v1alpha1.CatalogSource{ - ObjectMeta: metav1.ObjectMeta{Name: catalogSourceName, Namespace: namespace}, - Spec: v1alpha1.CatalogSourceSpec{ - // Catalog Sources that provide an address are not managed by the OLM controller - Address: "1.1.1.1", - }, - } - err := cl.Create(ctx, catalogSource) - Expect(err).NotTo(HaveOccurred()) - }) - It("manages the cache", func() { - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catSrcKey}) - Expect(res).To(Equal(ctrl.Result{RequeueAfter: unmanagedCatalogSourceSyncInterface})) - Expect(err).ToNot(HaveOccurred()) - - By("checking the cache is empty") - var entities []*input.Entity - err = reconciler.Iterate(ctx, func(entity *input.Entity) error { - entities = append(entities, entity) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - Expect(entities).To(BeEmpty()) - - By("updating the catalog source contents") - registryEntities := []*input.Entity{ - input.NewEntity(deppy.Identifier(fmt.Sprintf("%s/%s/pkg1/chan1/0.1.0", catalogSourceName, namespace)), map[string]string{}), - } - fakeRegistry.setEntitiesForSource(catSrcKey.String(), registryEntities...) - - By("re-running reconcile") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catSrcKey}) - Expect(res).To(Equal(ctrl.Result{RequeueAfter: unmanagedCatalogSourceSyncInterface})) - Expect(err).ToNot(HaveOccurred()) - - By("checking the cache is populated") - entities = nil - err = reconciler.Iterate(ctx, func(entity *input.Entity) error { - entities = append(entities, entity) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - Expect(entities).To(Equal(registryEntities)) - - By("checking an event has been emitted") - event := <-fakeRecorder.Events - Expect(event).To(Equal(fmt.Sprintf("Normal BundleCacheUpdated Successfully updated bundle cache from %s/%s", namespace, catalogSourceName))) - - By("deleting the catalog source") - Expect(cl.Delete(ctx, catalogSource)).To(Succeed()) - - By("re-running reconcile") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catSrcKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).ToNot(HaveOccurred()) - - By("checking the cache is empty again") - entities = nil - err = reconciler.Iterate(ctx, func(entity *input.Entity) error { - entities = append(entities, entity) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - Expect(entities).To(BeEmpty()) - }) - - It("re-enqueues on error", func() { - By("creating a fake registry that errors out") - fakeRegistry.setErrorForSource(catSrcKey.String(), fmt.Errorf("something bad happened")) - - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catSrcKey}) - Expect(res).To(Equal(ctrl.Result{Requeue: true})) - Expect(err).To(MatchError("something bad happened")) - - By("checking an event has been emitted") - event := <-fakeRecorder.Events - Expect(event).To(Equal(fmt.Sprintf("Warning BundleCacheUpdateFailed Failed to update bundle cache from %s/%s: something bad happened", namespace, catalogSourceName))) - }) - }) - }) - }) - Describe("querying CatalogSource EntitySource", func() { - var ( - catalogSource *v1alpha1.CatalogSource - catSrcKey types.NamespacedName - catalogSourceName string - namespace string - ) - BeforeEach(func() { - catalogSourceName = fmt.Sprintf("catalogsource-test-%s", rand.String(8)) - namespace = fmt.Sprintf("test-namespace-%s", rand.String(8)) - Expect(cl.Create(ctx, &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespace, - }, - })).To(Succeed()) - catSrcKey = types.NamespacedName{Name: catalogSourceName, Namespace: namespace} - - By("initializing cluster state") - catalogSource = &v1alpha1.CatalogSource{ - ObjectMeta: metav1.ObjectMeta{Name: catalogSourceName, Namespace: namespace}, - } - err := cl.Create(ctx, catalogSource) - Expect(err).NotTo(HaveOccurred()) - - registryEntities := []*input.Entity{ - input.NewEntity(deppy.Identifier(fmt.Sprintf("%s/%s/pkg1/chan1/0.1.0", catalogSourceName, namespace)), map[string]string{}), - input.NewEntity(deppy.Identifier(fmt.Sprintf("%s/%s/pkg1/chan1/0.2.0", catalogSourceName, namespace)), map[string]string{"k": "v"}), - } - fakeRegistry.setEntitiesForSource(catSrcKey.String(), registryEntities...) - - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catSrcKey}) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(Equal(ctrl.Result{})) - }) - - Describe("Get", func() { - It("should fetch an entity by ID", func() { - Expect(reconciler.Get(ctx, deppy.Identifier(fmt.Sprintf("%s/%s/pkg1/chan1/0.1.0", catalogSourceName, namespace)))).To( - Equal(input.NewEntity(deppy.Identifier(fmt.Sprintf("%s/%s/pkg1/chan1/0.1.0", catalogSourceName, namespace)), map[string]string{})), - ) - }) - It("should not fetch anything for nonexistent entity ID", func() { - Expect(reconciler.Get(ctx, "non-existent")).To(BeNil()) - }) - }) - Describe("Filter", func() { - It("should return entities that meet filter predicates", func() { - actual, err := reconciler.Filter(ctx, func(e *input.Entity) bool { - _, ok := e.Properties["k"] - return ok - }) - Expect(err).ToNot(HaveOccurred()) - Expect(actual).To(ConsistOf(input.EntityList{*input.NewEntity(deppy.Identifier(fmt.Sprintf("%s/%s/pkg1/chan1/0.2.0", catalogSourceName, namespace)), map[string]string{"k": "v"})})) - }) - }) - Describe("GroupBy", func() { - It("should group entities by the keys provided by the groupBy function", func() { - actual, err := reconciler.GroupBy(ctx, func(e *input.Entity) []string { - var keys []string - for k := range e.Properties { - keys = append(keys, k) - } - return keys - }) - Expect(err).ToNot(HaveOccurred()) - Expect(actual).To(Equal(input.EntityListMap{"k": input.EntityList{*input.NewEntity(deppy.Identifier(fmt.Sprintf("%s/%s/pkg1/chan1/0.2.0", catalogSourceName, namespace)), map[string]string{"k": "v"})}})) - }) - }) - Describe("Iterate", func() { - It("should go through all entities", func() { - var ids []string - Expect(reconciler.Iterate(ctx, func(e *input.Entity) error { - ids = append(ids, e.Identifier().String()) - return nil - })).To(BeNil()) - Expect(ids).To(ConsistOf([]string{fmt.Sprintf("%s/%s/pkg1/chan1/0.1.0", catalogSourceName, namespace), - fmt.Sprintf("%s/%s/pkg1/chan1/0.2.0", catalogSourceName, namespace)})) - }) - }) - }) -}) diff --git a/controllers/operator_controller.go b/controllers/operator_controller.go index 9c12e89e9..599248e71 100644 --- a/controllers/operator_controller.go +++ b/controllers/operator_controller.go @@ -65,7 +65,7 @@ type OperatorReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.1/pkg/reconcile func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - l := log.FromContext(ctx).WithName("operator-controller") + l := log.FromContext(ctx).WithName("reconcile") l.V(1).Info("starting") defer l.V(1).Info("ending") diff --git a/controllers/operator_controller_test.go b/controllers/operator_controller_test.go index 5e7b27c15..9d6cc2dde 100644 --- a/controllers/operator_controller_test.go +++ b/controllers/operator_controller_test.go @@ -24,7 +24,7 @@ import ( operatorutil "github.com/operator-framework/operator-controller/internal/util" ) -var _ = Describe("Operator Controller Test", func() { +var _ = Describe("Reconcile Test", func() { var ( ctx context.Context reconciler *controllers.OperatorReconciler @@ -875,19 +875,19 @@ func verifyConditionsInvariants(op *operatorsv1alpha1.Operator) { var testEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{ "operatorhub/prometheus/0.37.0": *input.NewEntity("operatorhub/prometheus/0.37.0", map[string]string{ - "olm.bundle.path": "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35", + "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35"`, "olm.channel": `{"channelName":"beta","priority":0}`, "olm.package": `{"packageName":"prometheus","version":"0.37.0"}`, "olm.gvk": `[]`, }), "operatorhub/prometheus/0.47.0": *input.NewEntity("operatorhub/prometheus/0.47.0", map[string]string{ - "olm.bundle.path": "quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed", + "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed"`, "olm.channel": `{"channelName":"beta","priority":0,"replaces":"prometheusoperator.0.37.0"}`, "olm.package": `{"packageName":"prometheus","version":"0.47.0"}`, "olm.gvk": `[]`, }), "operatorhub/badimage/0.1.0": *input.NewEntity("operatorhub/badimage/0.1.0", map[string]string{ - "olm.bundle.path": ``, + "olm.bundle.path": `{"name": "quay.io/operatorhubio/badimage:v0.1.0"}`, "olm.package": `{"packageName":"badimage","version":"0.1.0"}`, "olm.gvk": `[]`, }), diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 5d740dc22..556b0bc1c 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -22,9 +22,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/operator-framework/api/pkg/operators/v1alpha1" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" - v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" @@ -73,12 +71,6 @@ var _ = BeforeSuite(func() { err = rukpakv1alpha1.AddToScheme(sch) Expect(err).NotTo(HaveOccurred()) - // required for the catalog source controller tests - err = v1alpha1.AddToScheme(sch) - Expect(err).NotTo(HaveOccurred()) - err = v1.AddToScheme(sch) - Expect(err).NotTo(HaveOccurred()) - cl, err = client.New(cfg, client.Options{Scheme: sch}) Expect(err).NotTo(HaveOccurred()) Expect(cl).NotTo(BeNil()) diff --git a/go.mod b/go.mod index a12cd3236..4aad9963c 100644 --- a/go.mod +++ b/go.mod @@ -6,15 +6,10 @@ require ( github.com/blang/semver/v4 v4.0.0 github.com/onsi/ginkgo/v2 v2.8.3 github.com/onsi/gomega v1.27.1 - github.com/operator-framework/api v0.17.3 github.com/operator-framework/deppy v0.0.0-20230125110717-dc02e928470f github.com/operator-framework/operator-registry v1.26.2 github.com/operator-framework/rukpak v0.12.0 go.uber.org/zap v1.24.0 - golang.org/x/net v0.7.0 - google.golang.org/grpc v1.49.0 - k8s.io/api v0.26.1 - k8s.io/apiextensions-apiserver v0.26.1 k8s.io/apimachinery v0.26.1 k8s.io/client-go v0.26.1 k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 @@ -44,8 +39,6 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.2.0 // indirect - github.com/h2non/filetype v1.1.1 // indirect - github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -59,10 +52,10 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect @@ -71,11 +64,12 @@ require ( golang.org/x/tools v0.6.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.26.1 // indirect + k8s.io/apiextensions-apiserver v0.26.1 // indirect k8s.io/component-base v0.26.1 // indirect k8s.io/klog/v2 v2.80.1 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect diff --git a/go.sum b/go.sum index 04378985e..47d61d519 100644 --- a/go.sum +++ b/go.sum @@ -38,7 +38,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -56,11 +55,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -71,8 +65,6 @@ github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= @@ -81,7 +73,6 @@ github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-air/gini v1.0.4 h1:lteMAxHKNOAjIqazL/klOJJmxq6YxxSuJ17MnMXny+s= github.com/go-air/gini v1.0.4/go.mod h1:dd8RvT1xcv6N1da33okvBd8DhMh1/A4siGy6ErjTljs= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -158,7 +149,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -176,16 +166,10 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4= -github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= -github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c h1:fEE5/5VNnYUoBOj2I9TP8Jc+a7lge3QWn9DKE7NCwfc= -github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c/go.mod h1:ObS/W+h8RYb1Y7fYivughjxojTmIu5iAIjSrSLCLeqE= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -240,8 +224,6 @@ github.com/onsi/ginkgo/v2 v2.8.3 h1:RpbK1G8nWPNaCVFBWsOGnEQQGgASi6b8fxcWBvDYjxQ= github.com/onsi/ginkgo/v2 v2.8.3/go.mod h1:6OaUA8BCi0aZfmzYT/q9AacwTzDpNbxILUT+TlBq6MY= github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= -github.com/operator-framework/api v0.17.3 h1:wddE1SLKTNiIzwt28DbBIO+vPG2GOV6dkB9xBkDfT3o= -github.com/operator-framework/api v0.17.3/go.mod h1:34tb98EwTN5SZLkgoxwvRkhMJKLHUWHOrrcv1ZwvEeA= github.com/operator-framework/deppy v0.0.0-20230125110717-dc02e928470f h1:YxUZyQjF2kT2hli9ceBkuK7Mmiln0lV2RV38rzBObBI= github.com/operator-framework/deppy v0.0.0-20230125110717-dc02e928470f/go.mod h1:JaF7sX6tn7mpXcOehYjSHiKM1Y0z09vEfC6dca4AVuo= github.com/operator-framework/operator-registry v1.26.2 h1:kQToR/hPqdivljaRXM0olPllNIcc/GUk1VBoGwagJmk= @@ -280,13 +262,10 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -308,7 +287,6 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= @@ -383,7 +361,6 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -440,16 +417,12 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -462,7 +435,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= @@ -570,7 +542,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -578,8 +549,6 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -592,11 +561,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -609,8 +573,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -624,7 +586,6 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/resolution/entity_sources/hardcoded/bundle_cache.go b/internal/resolution/bundle_cache.go similarity index 99% rename from internal/resolution/entity_sources/hardcoded/bundle_cache.go rename to internal/resolution/bundle_cache.go index ae01a7a28..846d98565 100644 --- a/internal/resolution/entity_sources/hardcoded/bundle_cache.go +++ b/internal/resolution/bundle_cache.go @@ -1,4 +1,4 @@ -package hardcoded +package resolution import ( "github.com/operator-framework/deppy/pkg/deppy" diff --git a/internal/resolution/entity_sources/catalogsource/catalogsource_suite_test.go b/internal/resolution/entity_sources/catalogsource/catalogsource_suite_test.go deleted file mode 100644 index fefae720e..000000000 --- a/internal/resolution/entity_sources/catalogsource/catalogsource_suite_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package catalogsource_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestCatalogSource(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "CatalogSource Suite") -} diff --git a/internal/resolution/entity_sources/catalogsource/registry_bundle_to_entity.go b/internal/resolution/entity_sources/catalogsource/registry_bundle_to_entity.go deleted file mode 100644 index 52d641994..000000000 --- a/internal/resolution/entity_sources/catalogsource/registry_bundle_to_entity.go +++ /dev/null @@ -1,146 +0,0 @@ -package catalogsource - -import ( - "bytes" - "encoding/json" - "fmt" - "sort" - - "github.com/operator-framework/operator-registry/alpha/property" - catalogsourceapi "github.com/operator-framework/operator-registry/pkg/api" - "k8s.io/apimachinery/pkg/util/errors" - - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" -) - -type UpgradeEdge struct { - // possibly replaced by edge specific variablesources - // rather than being grouped with bundle properties - property.Channel - Replaces string `json:"replaces,omitempty"` - Skips []string `json:"skips,omitempty"` - SkipRange string `json:"skipRange,omitempty"` - Version string `json:"version,omitempty"` -} - -type DefaultChannel struct { - DefaultChannel string `json:"defaultchannel"` -} - -const ( - // TODO: reevaluate if defaultChannel is strictly necessary in olmv1 - typeDefaultChannel = "olm.package.defaultChannel" - typeBundleSource = "olm.bundle.path" -) - -func EntityFromBundle(catsrcID string, pkg *catalogsourceapi.Package, bundle *catalogsourceapi.Bundle) (*input.Entity, error) { - modelBundle, err := catalogsourceapi.ConvertAPIBundleToModelBundle(bundle) - if err != nil { - return nil, err - } - properties := map[string]string{} - var errs []error - - // Multivalue properties - propsList := map[string]map[string]struct{}{} - for _, p := range modelBundle.Properties { - switch p.Type { - case property.TypeBundleObject: - // ignore - only need metadata for resolution and bundle path for installation - case property.TypePackage: - properties[p.Type] = string(p.Value) - default: - var v interface{} - // the keys in the marshaled object may be out of order. - // recreate the json object so this doesn't happen. - pValue := p.Value - err := json.Unmarshal(p.Value, &v) - if err == nil { - // don't force property values to be json - // but if unmarshalled successfully, - // marshaling again should not fail. - pValue, err = jsonMarshal(v) - if err != nil { - errs = append(errs, err) - continue - } - } - if _, ok := propsList[p.Type]; !ok { - propsList[p.Type] = map[string]struct{}{} - } - if _, ok := propsList[p.Type][string(pValue)]; !ok { - propsList[p.Type][string(pValue)] = struct{}{} - } - } - } - - for pType, pValues := range propsList { - var prop []interface{} - for pValue := range pValues { - var v interface{} - err := json.Unmarshal([]byte(pValue), &v) - if err == nil { - // the property value may not be json. - // if unable to unmarshal, treat property value as a string - prop = append(prop, v) - } else { - prop = append(prop, pValue) - } - } - if len(prop) == 0 { - continue - } - if len(prop) > 1 { - sort.Slice(prop, func(i, j int) bool { - // enforce some ordering for deterministic properties. Possibly a neater way to do this. - return fmt.Sprintf("%v", prop[i]) < fmt.Sprintf("%v", prop[j]) - }) - } - pValue, err := jsonMarshal(prop) - if err != nil { - errs = append(errs, err) - continue - } - properties[pType] = string(pValue) - } - - upValue, err := jsonMarshal(UpgradeEdge{ - Channel: property.Channel{ - ChannelName: bundle.ChannelName, - }, - Replaces: bundle.Replaces, - Skips: bundle.Skips, - SkipRange: bundle.SkipRange, - }) - if err != nil { - errs = append(errs, err) - } else { - properties[property.TypeChannel] = string(upValue) - } - - properties[typeDefaultChannel] = pkg.DefaultChannelName - properties[typeBundleSource] = bundle.BundlePath - - if len(errs) > 0 { - return nil, fmt.Errorf("failed to parse properties for bundle %s/%s in %s: %v", bundle.GetPackageName(), bundle.GetVersion(), catsrcID, errors.NewAggregate(errs)) - } - - // Since multiple instances of bundle may exist for different channels, entityID must include reference to channel - return input.NewEntity(deppy.Identifier(fmt.Sprintf("%s/%s/%s/%s", catsrcID, bundle.PackageName, bundle.ChannelName, bundle.Version)), properties), nil -} - -func jsonMarshal(p interface{}) ([]byte, error) { - buf := &bytes.Buffer{} - dec := json.NewEncoder(buf) - dec.SetEscapeHTML(false) - err := dec.Encode(p) - if err != nil { - return nil, err - } - out := &bytes.Buffer{} - if err := json.Compact(out, buf.Bytes()); err != nil { - return nil, err - } - return out.Bytes(), nil -} diff --git a/internal/resolution/entity_sources/catalogsource/registry_bundle_to_entity_test.go b/internal/resolution/entity_sources/catalogsource/registry_bundle_to_entity_test.go deleted file mode 100644 index 03db6ba98..000000000 --- a/internal/resolution/entity_sources/catalogsource/registry_bundle_to_entity_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package catalogsource_test - -import ( - "github.com/operator-framework/deppy/pkg/deppy/input" - catalogsourceapi "github.com/operator-framework/operator-registry/pkg/api" - - "github.com/operator-framework/operator-controller/internal/resolution/entity_sources/catalogsource" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("RegistryBundleConverter", func() { - It("generates entity from bundle", func() { - // When converting a bundle to an entity, package, channel, defaultChannel and bundlePath - // must be created as special properties that are not overwritten - // The remaining entity properties must be aggregated from the bundle ProvidedAPIs, RequiredAPIs, - // Dependencies and Properties. - // Properties must be preserved except for `olm.bundle.object` properties, which are irrelevant to resolution. - // ProvidedAPIs, RequiredAPIs and Dependencies must be converted from their legacy format - // Values in the aggregated property set must not have duplicates. - b := &catalogsourceapi.Bundle{ - CsvName: "test", - PackageName: "other", - ChannelName: "beta", - BundlePath: "path/to/bundle", - Version: "0.1.4", - SkipRange: "< 0.1.4", - Replaces: "test-operator.v0.0.1", - Skips: []string{"test-operator.v0.0.2", "test-operator.v0.0.3"}, - ProvidedApis: []*catalogsourceapi.GroupVersionKind{{ - Group: "foo", - Version: "v1", - Kind: "prov1", - }, { - Group: "foo", - Version: "v1", - Kind: "prov2", - }}, - RequiredApis: []*catalogsourceapi.GroupVersionKind{{ - Group: "foo", - Version: "v1", - Kind: "req1", - }, { - Group: "foo", - Version: "v1", - Kind: "req2", - }, { - Group: "foo", - Version: "v1", - Kind: "req3", - }, { - Group: "foo", - Version: "v1", - Kind: "req4", - }}, - Dependencies: []*catalogsourceapi.Dependency{ - // legacy constraint types - { - Type: "olm.gvk", - Value: `{"group":"foo","version":"v1","kind":"req1"}`, - }, { - Type: "olm.gvk", - Value: `{"group":"foo","version":"v1","kind":"req2"}`, - }, { - Type: "olm.package", - Value: `{"packageName":"dep1","version":"1.1.0"}`, - }, { - Type: "olm.package", - Value: `{"packageName":"dep2","version":"<1.1.0"}`, - }}, - Properties: []*catalogsourceapi.Property{ - { - Type: "olm.package", - Value: `{"packageName":"test","version":"0.1.4"}`, - }, { - Type: "olm.gvk", - Value: `{"group":"foo","version":"v1","kind":"prov1"}`, - }, { - Type: "olm.gvk.required", - Value: `{"group":"foo","version":"v1","kind":"req1"}`, - }, { - Type: "olm.gvk.required", - Value: `{"group":"foo","version":"v1","kind":"req3"}`, - }, { - Type: "olm.package.required", - Value: `{"packageName":"dep1","versionRange":"1.1.0"}`, - }, { - Type: "olm.maxOpenShiftVersion", - Value: "4.12", - }, { - Type: "olm.deprecated", - Value: "{}", - }, { - //must be omitted - Type: "olm.bundle.object", - Value: `{"data": "eyJraW5kIjogIkN1c3RvbVJlc291cmNlRGVmaW5pdGlvbiIsICJhcGlWZXJzaW9uIjogImFwaWV4dGVuc2lvbnMuazhzLmlvL3YxIn0="}`, - }}, - } - p := &catalogsourceapi.Package{DefaultChannelName: "stable"} - entity, err := catalogsource.EntityFromBundle("test-catalog", p, b) - Expect(err).To(BeNil()) - Expect(entity).To(Equal(&input.Entity{ - ID: "test-catalog/other/beta/0.1.4", - Properties: map[string]string{ - "olm.package": `{"packageName":"test","version":"0.1.4"}`, - "olm.channel": `{"channelName":"beta","priority":0,"replaces":"test-operator.v0.0.1","skips":["test-operator.v0.0.2","test-operator.v0.0.3"],"skipRange":"< 0.1.4"}`, - "olm.package.defaultChannel": "stable", - "olm.bundle.path": "path/to/bundle", - "olm.maxOpenShiftVersion": "[4.12]", - "olm.deprecated": "[{}]", - "olm.package.required": `[{"packageName":"dep1","versionRange":"1.1.0"},{"packageName":"dep2","versionRange":"<1.1.0"}]`, - "olm.gvk": `[{"group":"foo","kind":"prov1","version":"v1"},{"group":"foo","kind":"prov2","version":"v1"}]`, - "olm.gvk.required": `[{"group":"foo","kind":"req1","version":"v1"},{"group":"foo","kind":"req2","version":"v1"},{"group":"foo","kind":"req3","version":"v1"},{"group":"foo","kind":"req4","version":"v1"}]`, - }, - })) - }) -}) diff --git a/internal/resolution/entity_sources/catalogsource/registry_grpc_client.go b/internal/resolution/entity_sources/catalogsource/registry_grpc_client.go deleted file mode 100644 index 1ddd62eb8..000000000 --- a/internal/resolution/entity_sources/catalogsource/registry_grpc_client.go +++ /dev/null @@ -1,194 +0,0 @@ -package catalogsource - -import ( - "context" - "errors" - "fmt" - "io" - "net" - "net/url" - "os" - "time" - - "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/deppy/pkg/deppy/input" - catalogsourceapi "github.com/operator-framework/operator-registry/pkg/api" - "golang.org/x/net/http/httpproxy" - "golang.org/x/net/proxy" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials/insecure" -) - -type RegistryClient interface { - ListEntities(ctx context.Context, catsrc *v1alpha1.CatalogSource) ([]*input.Entity, error) -} - -type registryGRPCClient struct { - timeout time.Duration -} - -func NewRegistryGRPCClient(grpcTimeout time.Duration) RegistryClient { - if grpcTimeout == 0 { - grpcTimeout = DefaultGRPCTimeout - } - return ®istryGRPCClient{timeout: grpcTimeout} -} - -func (r *registryGRPCClient) ListEntities(ctx context.Context, catalogSource *v1alpha1.CatalogSource) ([]*input.Entity, error) { - // TODO: create GRPC connections separately - conn, err := ConnectGRPCWithTimeout(ctx, catalogSource.Address(), r.timeout) - if conn != nil { - // ConnectGRPCWithTimeout may fail to establish a READY connection - // close any opened connection regardless of its state. - defer conn.Close() - } - if err != nil { - return nil, err - } - - catsrcClient := catalogsourceapi.NewRegistryClient(conn) - stream, err := catsrcClient.ListBundles(ctx, &catalogsourceapi.ListBundlesRequest{}) - - if err != nil { - return nil, fmt.Errorf("ListBundles failed: %v", err) - } - - var entities []*input.Entity - catalogPackages := map[string]*catalogsourceapi.Package{} - catalogSourceID := fmt.Sprintf("%s/%s", catalogSource.Namespace, catalogSource.Name) - for { - bundle, err := stream.Recv() - if errors.Is(err, io.EOF) { - break - } - if err != nil { - return nil, fmt.Errorf("failed to read bundle stream: %v", err) - } - - packageKey := fmt.Sprintf("%s/%s", catalogSourceID, bundle.PackageName) - pkg, ok := catalogPackages[packageKey] - if !ok { - pkg, err = catsrcClient.GetPackage(ctx, &catalogsourceapi.GetPackageRequest{Name: bundle.PackageName}) - if err != nil { - return nil, fmt.Errorf("failed to get package %s: %v", bundle.PackageName, err) - } - catalogPackages[packageKey] = pkg - } - - entity, err := EntityFromBundle(catalogSourceID, pkg, bundle) - if err != nil { - return nil, fmt.Errorf("failed to parse entity %s: %v", entity.Identifier(), err) - } - entities = append(entities, entity) - } - return entities, nil -} - -const DefaultGRPCTimeout = 2 * time.Minute - -func ConnectGRPCWithTimeout(ctx context.Context, address string, timeout time.Duration) (conn *grpc.ClientConn, err error) { - // based on https://github.com/operator-framework/operator-lifecycle-manager/blob/afc0848d102ecdc01a0b0f3b55d389bb66acf168/pkg/controller/registry/grpc/source.go#L149 - conn, err = grpcConnection(address) - if err != nil { - return nil, fmt.Errorf("GRPC connection failed: %v", err) - } - - if timeout == 0 { - timeout = DefaultGRPCTimeout - } - - if err := waitForGRPCWithTimeout(ctx, conn, timeout, address); err != nil { - return conn, fmt.Errorf("GRPC timeout: %v", err) - } - - return conn, nil -} - -func waitForGRPCWithTimeout(ctx context.Context, conn *grpc.ClientConn, timeout time.Duration, address string) error { - if conn == nil { - return fmt.Errorf("nil connection") - } - state := conn.GetState() - if state == connectivity.Ready { - return nil - } - oldState := state - ctx2, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - for { - select { - case <-ctx2.Done(): - return fmt.Errorf("%v %s timed out waiting for ready state, %v", time.Now(), address, timeout) - default: - state := conn.GetState() - if state != oldState { - oldState = state - } - if state == connectivity.Ready { - return nil - } - } - } -} - -func grpcConnection(address string) (*grpc.ClientConn, error) { - dialOptions := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} - proxyURL, err := grpcProxyURL(address) - if err != nil { - return nil, err - } - - if proxyURL != nil { - dialOptions = append(dialOptions, grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { - dialer, err := proxy.FromURL(proxyURL, &net.Dialer{}) - if err != nil { - return nil, err - } - return dialer.Dial("tcp", addr) - })) - } - - return grpc.Dial(address, dialOptions...) -} - -func grpcProxyURL(addr string) (*url.URL, error) { - // Handle ip addresses - host, _, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - - url, err := url.Parse(host) - if err != nil { - return nil, err - } - - // Hardcode fields required for proxy resolution - url.Host = addr - url.Scheme = "http" - - // Override HTTPS_PROXY and HTTP_PROXY with GRPC_PROXY - proxyConfig := &httpproxy.Config{ - HTTPProxy: getGRPCProxyEnv(), - HTTPSProxy: getGRPCProxyEnv(), - NoProxy: getEnvAny("NO_PROXY", "no_proxy"), - CGI: os.Getenv("REQUEST_METHOD") != "", - } - - // Check if a proxy should be used based on environment variables - return proxyConfig.ProxyFunc()(url) -} - -func getGRPCProxyEnv() string { - return getEnvAny("GRPC_PROXY", "grpc_proxy") -} - -func getEnvAny(names ...string) string { - for _, n := range names { - if val := os.Getenv(n); val != "" { - return val - } - } - return "" -} diff --git a/internal/resolution/entity_sources/catalogsource/registry_grpc_client_test.go b/internal/resolution/entity_sources/catalogsource/registry_grpc_client_test.go deleted file mode 100644 index 10d0513fb..000000000 --- a/internal/resolution/entity_sources/catalogsource/registry_grpc_client_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package catalogsource_test - -import ( - "context" - "net" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/deppy/pkg/deppy/input" - "github.com/operator-framework/operator-registry/pkg/api" - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" - - "github.com/operator-framework/operator-controller/internal/resolution/entity_sources/catalogsource" -) - -type mockServer struct { - packages map[string]*api.Package - bundles []*api.Bundle - api.UnimplementedRegistryServer -} - -func (s *mockServer) ListBundles(_ *api.ListBundlesRequest, stream api.Registry_ListBundlesServer) error { - for _, b := range s.bundles { - if err := stream.Send(b); err != nil { - return err - } - } - return nil -} - -func (s *mockServer) GetPackage(_ context.Context, req *api.GetPackageRequest) (*api.Package, error) { - if req != nil { - if p, ok := s.packages[req.Name]; ok { - return p, nil - } - } - return &api.Package{}, nil -} - -var _ = Describe("Registry GRPC Client", func() { - testPackages := map[string]*api.Package{ - "prometheus": { - Name: "prometheus", - Channels: []*api.Channel{{ - Name: "beta", - CsvName: "prometheusoperator.0.47.0", - }}, - DefaultChannelName: "beta", - }, - } - testBundles := []*api.Bundle{{ - CsvName: "prometheusoperator.0.47.0", - PackageName: "prometheus", - ChannelName: "beta", - BundlePath: "quay.io/openshift-community-operators/prometheus@sha256:f0fdb1a53526bb9d3761ac6c1ae30bc73539a544efd1ce099e3d1893fadc9c6b", - ProvidedApis: []*api.GroupVersionKind{{ - Group: "monitoring.coreos.com", - Version: "v1", - Kind: "Prometheus", - Plural: "prometheuses", - }, { - Group: "monitoring.coreos.com", - Version: "v1", - Kind: "PrometheusRule", - Plural: "prometheusrules", - }, { - Group: "monitoring.coreos.com", - Version: "v1", - Kind: "Probe", - Plural: "probes", - }}, - Version: "0.47.0", - Properties: []*api.Property{ - { - Type: "olm.gvk", - Value: `{"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1"}`, - }, { - Type: "olm.gvk", - Value: `{"group":"monitoring.coreos.com","kind":"PrometheusRule","version":"v1"}`, - }, { - Type: "olm.maxOpenShiftVersion", - Value: `"4.8"`, - }, { - Type: "olm.package", - Value: `{"packageName":"prometheus","version":"0.47.0"}`, - }, - }, - Replaces: "prometheusoperator.0.37.0", - }, { - CsvName: "prometheusoperator.0.37.0", - PackageName: "prometheus", - ChannelName: "beta", - BundlePath: "quay.io/openshift-community-operators/prometheus@sha256:6fbd3eaa123054c5023323d1f9ab7cbea178087fcb7cb4f3e83872c6a88d39a1", - ProvidedApis: []*api.GroupVersionKind{{ - Group: "monitoring.coreos.com", - Version: "v1", - Kind: "Prometheus", - Plural: "prometheuses", - }, { - Group: "monitoring.coreos.com", - Version: "v1", - Kind: "PrometheusRule", - Plural: "prometheusrules", - }}, - Version: "0.37.0", - Properties: []*api.Property{ - { - Type: "olm.gvk", - Value: `{"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1"}`, - }, { - Type: "olm.gvk", - Value: `{"group":"monitoring.coreos.com","kind":"PrometheusRule","version":"v1"}`, - }, { - Type: "olm.package", - Value: `{"packageName":"prometheus","version":"0.37.0"}`, - }, - }, - }} - - var grpcServer *grpc.Server - done := make(chan struct{}) - - BeforeEach(func() { - lis, err := net.Listen("tcp", ":50052") - Expect(err).To(BeNil()) - - grpcServer = grpc.NewServer() - api.RegisterRegistryServer(grpcServer, &mockServer{packages: testPackages, bundles: testBundles}) - - reflection.Register(grpcServer) - - go func() { - err = grpcServer.Serve(lis) // run till gracefulStop is called - Expect(err).To(BeNil()) - close(done) - }() - }) - - AfterEach(func() { - grpcServer.GracefulStop() - - <-done - }) - - It("lists entities from a grpc registry server", func() { - entities, err := catalogsource.NewRegistryGRPCClient(1*time.Minute).ListEntities(context.TODO(), &v1alpha1.CatalogSource{ - Spec: v1alpha1.CatalogSourceSpec{ - Address: ":50052", - }, - }) - Expect(err).To(BeNil()) - - Expect(entities).To(ConsistOf([]*input.Entity{{ - ID: "//prometheus/beta/0.47.0", - Properties: map[string]string{ - "olm.gvk": `[{"group":"monitoring.coreos.com","kind":"Probe","version":"v1"},{"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1"},{"group":"monitoring.coreos.com","kind":"PrometheusRule","version":"v1"}]`, - "olm.maxOpenShiftVersion": `["4.8"]`, - "olm.package": `{"packageName":"prometheus","version":"0.47.0"}`, - "olm.channel": `{"channelName":"beta","priority":0,"replaces":"prometheusoperator.0.37.0"}`, - "olm.package.defaultChannel": "beta", - "olm.bundle.path": "quay.io/openshift-community-operators/prometheus@sha256:f0fdb1a53526bb9d3761ac6c1ae30bc73539a544efd1ce099e3d1893fadc9c6b", - }, - }, { - ID: "//prometheus/beta/0.37.0", - Properties: map[string]string{ - "olm.gvk": `[{"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1"},{"group":"monitoring.coreos.com","kind":"PrometheusRule","version":"v1"}]`, - "olm.package": `{"packageName":"prometheus","version":"0.37.0"}`, - "olm.channel": `{"channelName":"beta","priority":0}`, - "olm.package.defaultChannel": "beta", - "olm.bundle.path": "quay.io/openshift-community-operators/prometheus@sha256:6fbd3eaa123054c5023323d1f9ab7cbea178087fcb7cb4f3e83872c6a88d39a1", - }, - }})) - }) -}) diff --git a/internal/resolution/resolver.go b/internal/resolution/resolver.go index 1ffd1a221..a074e7bf6 100644 --- a/internal/resolution/resolver.go +++ b/internal/resolution/resolver.go @@ -7,9 +7,8 @@ import ( "github.com/operator-framework/deppy/pkg/deppy/solver" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/olm" - "github.com/operator-framework/operator-controller/api/v1alpha1" + "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/olm" ) type OperatorResolver struct { diff --git a/internal/resolution/variable_sources/entity/bundle_entity.go b/internal/resolution/variable_sources/entity/bundle_entity.go index 0bcf160fb..cadb79d65 100644 --- a/internal/resolution/variable_sources/entity/bundle_entity.go +++ b/internal/resolution/variable_sources/entity/bundle_entity.go @@ -10,10 +10,7 @@ import ( "github.com/operator-framework/operator-registry/alpha/property" ) -const ( - propertyBundlePath = "olm.bundle.path" - propertyDefaultChannel = "olm.package.defaultChannel" -) +const PropertyBundlePath = "olm.bundle.path" type ChannelProperties struct { property.Channel @@ -60,6 +57,7 @@ type BundleEntity struct { requiredPackages []PackageRequired channelProperties *ChannelProperties semVersion *semver.Version + bundlePath string mu sync.RWMutex } @@ -120,17 +118,10 @@ func (b *BundleEntity) ChannelProperties() (*ChannelProperties, error) { } func (b *BundleEntity) BundlePath() (string, error) { - if bundlePath, ok := b.Entity.Properties[propertyBundlePath]; ok && bundlePath != "" { - return bundlePath, nil - } - return "", fmt.Errorf("error determining bundle path for entity '%s': property '%s' not found or empty", b.ID, propertyBundlePath) -} - -func (b *BundleEntity) DefaultChannel() (string, error) { - if defaultChannel, ok := b.Entity.Properties[propertyDefaultChannel]; ok && defaultChannel != "" { - return defaultChannel, nil + if err := b.loadBundlePath(); err != nil { + return "", err } - return "", fmt.Errorf("error determining default channel for entity '%s': property '%s' not found or empty", b.ID, propertyDefaultChannel) + return b.bundlePath, nil } func (b *BundleEntity) loadPackage() error { @@ -212,6 +203,19 @@ func (b *BundleEntity) loadChannelProperties() error { return nil } +func (b *BundleEntity) loadBundlePath() error { + b.mu.Lock() + defer b.mu.Unlock() + if b.bundlePath == "" { + bundlePath, err := loadFromEntity[string](b.Entity, PropertyBundlePath, required) + if err != nil { + return fmt.Errorf("error determining bundle path for entity '%s': %w", b.ID, err) + } + b.bundlePath = bundlePath + } + return nil +} + func loadFromEntity[T interface{}](entity *input.Entity, propertyName string, required propertyRequirement) (T, error) { deserializedProperty := *new(T) propertyValue, ok := entity.Properties[propertyName] diff --git a/internal/resolution/variable_sources/entity/bundle_entity_test.go b/internal/resolution/variable_sources/entity/bundle_entity_test.go index f610041cf..16aa5e87c 100644 --- a/internal/resolution/variable_sources/entity/bundle_entity_test.go +++ b/internal/resolution/variable_sources/entity/bundle_entity_test.go @@ -243,7 +243,7 @@ var _ = Describe("BundleEntity", func() { Describe("BundlePath", func() { It("should return the bundle channel properties if present", func() { entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{ - "olm.bundle.path": "bundle.io/path/to/bundle", + "olm.bundle.path": `"bundle.io/path/to/bundle"`, }) bundleEntity := olmentity.NewBundleEntity(entity) bundlePath, err := bundleEntity.BundlePath() @@ -255,44 +255,16 @@ var _ = Describe("BundleEntity", func() { bundleEntity := olmentity.NewBundleEntity(entity) bundlePath, err := bundleEntity.BundlePath() Expect(bundlePath).To(BeEmpty()) - Expect(err.Error()).To(Equal("error determining bundle path for entity 'operatorhub/prometheus/0.14.0': property 'olm.bundle.path' not found or empty")) + Expect(err.Error()).To(Equal("error determining bundle path for entity 'operatorhub/prometheus/0.14.0': required property 'olm.bundle.path' not found")) }) - It("should return error if the property is empty", func() { + It("should return error if the property is malformed", func() { entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{ - "olm.bundle.path": "", + "olm.bundle.path": "badBundlePath", }) bundleEntity := olmentity.NewBundleEntity(entity) bundlePath, err := bundleEntity.BundlePath() Expect(bundlePath).To(BeEmpty()) - Expect(err.Error()).To(Equal("error determining bundle path for entity 'operatorhub/prometheus/0.14.0': property 'olm.bundle.path' not found or empty")) - }) - }) - - Describe("DefaultChannel", func() { - It("should return the bundle channel properties if present", func() { - entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{ - "olm.package.defaultChannel": "stable", - }) - bundleEntity := olmentity.NewBundleEntity(entity) - defaultChannel, err := bundleEntity.DefaultChannel() - Expect(err).ToNot(HaveOccurred()) - Expect(defaultChannel).To(Equal("stable")) - }) - It("should return an error if the property is not found", func() { - entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{}) - bundleEntity := olmentity.NewBundleEntity(entity) - defaultChannel, err := bundleEntity.DefaultChannel() - Expect(defaultChannel).To(BeEmpty()) - Expect(err.Error()).To(Equal("error determining default channel for entity 'operatorhub/prometheus/0.14.0': property 'olm.package.defaultChannel' not found or empty")) - }) - It("should return error if the property is empty", func() { - entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{ - "olm.bundle.path": "", - }) - bundleEntity := olmentity.NewBundleEntity(entity) - defaultChannel, err := bundleEntity.DefaultChannel() - Expect(defaultChannel).To(BeEmpty()) - Expect(err.Error()).To(Equal("error determining default channel for entity 'operatorhub/prometheus/0.14.0': property 'olm.package.defaultChannel' not found or empty")) + Expect(err.Error()).To(Equal("error determining bundle path for entity 'operatorhub/prometheus/0.14.0': property 'olm.bundle.path' ('badBundlePath') could not be parsed: invalid character 'b' looking for beginning of value")) }) }) }) diff --git a/main.go b/main.go index 0c26d4b35..9182f0bb6 100644 --- a/main.go +++ b/main.go @@ -20,7 +20,6 @@ import ( "flag" "os" - olmv0v1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" "go.uber.org/zap/zapcore" "k8s.io/apimachinery/pkg/runtime" @@ -43,12 +42,10 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme)) utilruntime.Must(rukpakv1alpha1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme - - // required by the catalog source cache controller - utilruntime.Must(olmv0v1alpha1.AddToScheme(scheme)) } func main() { @@ -92,20 +89,10 @@ func main() { os.Exit(1) } - catsrcReconciler := controllers.NewCatalogSourceReconciler( - mgr.GetClient(), - mgr.GetScheme(), - mgr.GetEventRecorderFor("catalogsource-controller"), - ) - if err := catsrcReconciler.SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create catalog source controller", "controller", "CatalogSource") - os.Exit(1) - } - if err = (&controllers.OperatorReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - Resolver: resolution.NewOperatorResolver(mgr.GetClient(), catsrcReconciler), + Resolver: resolution.NewOperatorResolver(mgr.GetClient(), resolution.HardcodedEntitySource), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Operator") os.Exit(1) diff --git a/test/e2e/catalogsource_install_test.go b/test/e2e/catalogsource_install_test.go deleted file mode 100644 index 25ba68cd7..000000000 --- a/test/e2e/catalogsource_install_test.go +++ /dev/null @@ -1,228 +0,0 @@ -package e2e - -import ( - "context" - "fmt" - "os" - - . "github.com/onsi/gomega" - - "github.com/operator-framework/api/pkg/operators/v1alpha1" - corev1 "k8s.io/api/core/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextensionsscheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - yaml2 "k8s.io/apimachinery/pkg/util/yaml" - "sigs.k8s.io/controller-runtime/pkg/client" - controllerClient "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" -) - -// ListEntities -func createTestNamespace(ctx context.Context, c client.Client, prefix string) string { - ns := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: prefix, - }, - } - err := c.Create(ctx, ns) - Expect(err).To(BeNil()) - return ns.Name -} - -func deleteTestNamespace(ctx context.Context, c client.Client, name string) { - err := c.Delete(ctx, &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - }) - Expect(err).To(BeNil()) -} - -func createTestServiceAccount(ctx context.Context, c client.Client, namespace, prefix string) string { - sa := &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceAccount", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - GenerateName: prefix, - Namespace: namespace, - }, - } - err := c.Create(ctx, sa) - Expect(err).To(BeNil()) - return sa.Name -} - -func createTestRegistryPod(ctx context.Context, cli client.Client, namespace, prefix, serviceAccount string) string { - pod := &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - GenerateName: prefix, - Labels: map[string]string{"catalogsource": "prometheus-index"}, - Annotations: nil, - Namespace: namespace, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "registry", - // TODO: switch to using locally built and loaded images to avoid flakes - Image: "quay.io/ankitathomas/index:quay", - Ports: []corev1.ContainerPort{ - { - Name: "grpc", - ContainerPort: 50051, - }, - }, - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"grpc_health_probe", "-addr=:50051"}, - }, - }, - InitialDelaySeconds: 5, - TimeoutSeconds: 5, - }, - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"grpc_health_probe", "-addr=:50051"}, - }, - }, - InitialDelaySeconds: 10, - TimeoutSeconds: 5, - }, - StartupProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"grpc_health_probe", "-addr=:50051"}, - }, - }, - FailureThreshold: 15, - PeriodSeconds: 10, - }, - ImagePullPolicy: corev1.PullAlways, - TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, - }}, - ServiceAccountName: serviceAccount, - }} - err := cli.Create(ctx, pod) - Expect(err).To(BeNil()) - - currentPod := corev1.Pod{} - Eventually(func() (bool, error) { - err := cli.Get(ctx, types.NamespacedName{Name: pod.Name, Namespace: namespace}, ¤tPod) - if err != nil { - return false, err - } - if len(currentPod.Status.ContainerStatuses) == 0 { - return false, fmt.Errorf("pod not ready") - } - return currentPod.Status.ContainerStatuses[0].Ready, nil - }, "5m", "1s", ctx).Should(BeTrue()) - return pod.Name -} - -func createTestRegistryService(ctx context.Context, cli client.Client, namespace, prefix string) string { - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - GenerateName: prefix, - Namespace: namespace, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: "grpc", - Port: 50051, - TargetPort: intstr.FromInt(50051), - }, - }, - Selector: map[string]string{"catalogsource": "prometheus-index"}, - }, - } - err := c.Create(ctx, svc) - Expect(err).To(BeNil()) - - return svc.Name -} - -func getServiceAddress(ctx context.Context, cli client.Client, namespace, name string) string { - svc := corev1.Service{} - err := cli.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, &svc) - Expect(err).To(BeNil()) - return fmt.Sprintf("%s.%s.svc:%d", svc.Name, svc.Namespace, svc.Spec.Ports[0].Port) -} - -// TODO: ensure CRD support in test environment setup in Makefile -func applyCRDifNotPresent(ctx context.Context) func() { - cleanup := func() {} - scheme := runtime.NewScheme() - //Add CRDs - err := apiextensionsscheme.AddToScheme(scheme) - Expect(err).To(BeNil()) - - c := config.GetConfigOrDie() - ctrlCli, err := controllerClient.New(c, controllerClient.Options{ - Scheme: scheme, - }) - Expect(err).To(BeNil()) - - catalogSourceCRD := apiextensionsv1.CustomResourceDefinition{} - err = ctrlCli.Get(ctx, types.NamespacedName{Name: "catalogsources.operators.coreos.com"}, &catalogSourceCRD) - if err != nil { - Expect(errors.IsNotFound(err)).To(BeTrue()) - crdContents, err := os.ReadFile("../../testdata/crds/operators.coreos.com_catalogsources.yaml") - Expect(err).To(BeNil()) - - err = yaml2.Unmarshal(crdContents, &catalogSourceCRD) - Expect(err).To(BeNil()) - - err = ctrlCli.Create(ctx, &catalogSourceCRD) - if !errors.IsAlreadyExists(err) { - Expect(err).To(BeNil()) - // cleanup catalogsource crd only if it didn't already exist on-cluster - cleanup = func() { - err = ctrlCli.Delete(ctx, &apiextensionsv1.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: "catalogsources.operators.coreos.com"}}) - Expect(err).To(BeNil()) - } - } - } - return cleanup -} - -func createTestCatalogSource(ctx context.Context, cli client.Client, namespace, name, serviceName string) { - scheme := runtime.NewScheme() - // Add catalogSources - err := v1alpha1.AddToScheme(scheme) - Expect(err).To(BeNil()) - - c := config.GetConfigOrDie() - ctrlCli, err := controllerClient.New(c, controllerClient.Options{ - Scheme: scheme, - }) - Expect(err).To(BeNil()) - - err = ctrlCli.Create(ctx, &v1alpha1.CatalogSource{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: v1alpha1.CatalogSourceSpec{ - Address: getServiceAddress(ctx, cli, namespace, serviceName), - SourceType: v1alpha1.SourceTypeGrpc, - }, - }) - Expect(err).To(BeNil()) -} diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index a11e8671a..15b98a628 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -6,13 +6,11 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - catsrcapi "github.com/operator-framework/api/pkg/operators/v1alpha1" operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" ) @@ -39,12 +37,6 @@ var _ = BeforeSuite(func() { err = rukpakv1alpha1.AddToScheme(scheme) Expect(err).To(Not(HaveOccurred())) - err = catsrcapi.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - err = corev1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - c, err = client.New(cfg, client.Options{Scheme: scheme}) Expect(err).To(Not(HaveOccurred())) }) diff --git a/test/e2e/install_test.go b/test/e2e/install_test.go index 89a8cc51d..2262b3709 100644 --- a/test/e2e/install_test.go +++ b/test/e2e/install_test.go @@ -27,28 +27,9 @@ var _ = Describe("Operator Install", func() { operatorName string operator *operatorv1alpha1.Operator ) - - var testNamespace string - cleanup := func() {} - ctx = context.TODO() - BeforeEach(func() { - testNamespace = createTestNamespace(ctx, c, "registry-grpc-") - cleanup = applyCRDifNotPresent(ctx) - testPrefix := "registry-grpc-" - - serviceAccountName := createTestServiceAccount(ctx, c, testNamespace, testPrefix) - createTestRegistryPod(ctx, c, testNamespace, testPrefix, serviceAccountName) - serviceName := createTestRegistryService(ctx, c, testNamespace, testPrefix) - createTestCatalogSource(ctx, c, testNamespace, "prometheus-index", serviceName) - - }) - AfterEach(func() { - deleteTestNamespace(ctx, c, testNamespace) - cleanup() - }) It("resolves the specified package with correct bundle path", func() { ctx = context.Background() - pkgName = "project-quay" + pkgName = "prometheus" operatorName = fmt.Sprintf("operator-%s", rand.String(8)) operator = &operatorv1alpha1.Operator{ ObjectMeta: metav1.ObjectMeta{ @@ -63,6 +44,8 @@ var _ = Describe("Operator Install", func() { err := c.Create(ctx, operator) Expect(err).ToNot(HaveOccurred()) + // TODO dfranz: This test currently relies on the hard-coded CatalogSources found in bundle_cache.go + // and should be re-worked to use a real or test catalog source when the hard-coded stuff is removed By("eventually reporting a successful resolution and bundle path") Eventually(func(g Gomega) { err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator) diff --git a/testdata/crds/operators.coreos.com_catalogsources.yaml b/testdata/crds/operators.coreos.com_catalogsources.yaml deleted file mode 100644 index aa78265ab..000000000 --- a/testdata/crds/operators.coreos.com_catalogsources.yaml +++ /dev/null @@ -1,257 +0,0 @@ -## TODO perdasilva: remove this file and add a crds package in olm so we can grab it from that repo instead of having two copies ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.0 - creationTimestamp: null - name: catalogsources.operators.coreos.com -spec: - group: operators.coreos.com - names: - categories: - - olm - kind: CatalogSource - listKind: CatalogSourceList - plural: catalogsources - shortNames: - - catsrc - singular: catalogsource - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The pretty name of the catalog - jsonPath: .spec.displayName - name: Display - type: string - - description: The type of the catalog - jsonPath: .spec.sourceType - name: Type - type: string - - description: The publisher of the catalog - jsonPath: .spec.publisher - name: Publisher - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: CatalogSource is a repository of CSVs, CRDs, and operator packages. - type: object - required: - - metadata - - spec - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - type: object - required: - - sourceType - properties: - address: - description: 'Address is a host that OLM can use to connect to a pre-existing registry. Format: : Only used when SourceType = SourceTypeGrpc. Ignored when the Image field is set.' - type: string - configMap: - description: ConfigMap is the name of the ConfigMap to be used to back a configmap-server registry. Only used when SourceType = SourceTypeConfigmap or SourceTypeInternal. - type: string - description: - type: string - displayName: - description: Metadata - type: string - grpcPodConfig: - description: GrpcPodConfig exposes different overrides for the pod spec of the CatalogSource Pod. Only used when SourceType = SourceTypeGrpc and Image is set. - type: object - properties: - nodeSelector: - description: NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. - type: object - additionalProperties: - type: string - priorityClassName: - description: If specified, indicates the pod's priority. If not specified, the pod priority will be default or zero if there is no default. - type: string - securityContextConfig: - description: "SecurityContextConfig can be one of `legacy` or `restricted`. The CatalogSource's pod is either injected with the right pod.spec.securityContext and pod.spec.container[*].securityContext values to allow the pod to run in Pod Security Admission (PSA) `restricted` mode, or doesn't set these values at all, in which case the pod can only be run in PSA `baseline` or `privileged` namespaces. Currently if the SecurityContextConfig is unspecified, the default value of `legacy` is used. Specifying a value other than `legacy` or `restricted` result in a validation error. When using older catalog images, which could not be run in `restricted` mode, the SecurityContextConfig should be set to `legacy`. \n In a future version will the default will be set to `restricted`, catalog maintainers should rebuild their catalogs with a version of opm that supports running catalogSource pods in `restricted` mode to prepare for these changes. \n More information about PSA can be found here: https://kubernetes.io/docs/concepts/security/pod-security-admission/'" - type: string - default: legacy - enum: - - legacy - - restricted - tolerations: - description: Tolerations are the catalog source's pod's tolerations. - type: array - items: - description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . - type: object - properties: - effect: - description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. - type: integer - format: int64 - value: - description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - icon: - type: object - required: - - base64data - - mediatype - properties: - base64data: - type: string - mediatype: - type: string - image: - description: Image is an operator-registry container image to instantiate a registry-server with. Only used when SourceType = SourceTypeGrpc. If present, the address field is ignored. - type: string - priority: - description: 'Priority field assigns a weight to the catalog source to prioritize them so that it can be consumed by the dependency resolver. Usage: Higher weight indicates that this catalog source is preferred over lower weighted catalog sources during dependency resolution. The range of the priority value can go from positive to negative in the range of int32. The default value to a catalog source with unassigned priority would be 0. The catalog source with the same priority values will be ranked lexicographically based on its name.' - type: integer - publisher: - type: string - secrets: - description: Secrets represent set of secrets that can be used to access the contents of the catalog. It is best to keep this list small, since each will need to be tried for every catalog entry. - type: array - items: - type: string - sourceType: - description: SourceType is the type of source - type: string - updateStrategy: - description: UpdateStrategy defines how updated catalog source images can be discovered Consists of an interval that defines polling duration and an embedded strategy type - type: object - properties: - registryPoll: - type: object - properties: - interval: - description: Interval is used to determine the time interval between checks of the latest catalog source version. The catalog operator polls to see if a new version of the catalog source is available. If available, the latest image is pulled and gRPC traffic is directed to the latest catalog source. - type: string - status: - type: object - properties: - conditions: - description: Represents the state of a CatalogSource. Note that Message and Reason represent the original status information, which may be migrated to be conditions based in the future. Any new features introduced will use conditions. - type: array - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - type: object - required: - - lastTransitionTime - - message - - reason - - status - - type - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - type: string - format: date-time - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - type: string - maxLength: 32768 - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - type: integer - format: int64 - minimum: 0 - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - type: string - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - status: - description: status of the condition, one of True, False, Unknown. - type: string - enum: - - "True" - - "False" - - Unknown - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - type: string - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - configMapReference: - type: object - required: - - name - - namespace - properties: - lastUpdateTime: - type: string - format: date-time - name: - type: string - namespace: - type: string - resourceVersion: - type: string - uid: - description: UID is a type that holds unique ID values, including UUIDs. Because we don't ONLY use UUIDs, this is an alias to string. Being a type captures intent and helps make sure that UIDs and names do not get conflated. - type: string - connectionState: - type: object - required: - - lastObservedState - properties: - address: - type: string - lastConnect: - type: string - format: date-time - lastObservedState: - type: string - latestImageRegistryPoll: - description: The last time the CatalogSource image registry has been polled to ensure the image is up-to-date - type: string - format: date-time - message: - description: A human readable message indicating details about why the CatalogSource is in this condition. - type: string - reason: - description: Reason is the reason the CatalogSource was transitioned to its current state. - type: string - registryService: - type: object - properties: - createdAt: - type: string - format: date-time - port: - type: string - protocol: - type: string - serviceName: - type: string - serviceNamespace: - type: string - served: true - storage: true - subresources: - status: {}