diff --git a/Makefile b/Makefile index c6ff38d35..a9442622d 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,8 @@ export IMAGE_REPO ?= quay.io/operator-framework/operator-controller export IMAGE_TAG ?= devel export GO_BUILD_TAGS ?= upstream export CERT_MGR_VERSION ?= v1.9.0 +export CATALOGD_VERSION ?= v0.1.3 export GORELEASER_VERSION ?= v1.16.2 -export OLM_V0_VERSION ?= v0.24.0 export RUKPAK_VERSION=$(shell go list -mod=mod -m -f "{{.Version}}" github.com/operator-framework/rukpak) export WAIT_TIMEOUT ?= 60s IMG?=$(IMAGE_REPO):$(IMAGE_TAG) @@ -147,7 +147,7 @@ release: goreleaser ## Runs goreleaser for the operator-controller. By default, quickstart: export MANIFEST="https://github.com/operator-framework/operator-controller/releases/download/$(VERSION)/operator-controller.yaml" quickstart: kustomize generate ## Generate the installation release manifests and scripts kubectl kustomize config/default | sed "s/:devel/:$(VERSION)/g" > operator-controller.yaml - envsubst '$$OLM_V0_VERSION,$$CERT_MGR_VERSION,$$RUKPAK_VERSION,$$MANIFEST' < scripts/install.tpl.sh > install.sh + envsubst '$$CATALOGD_VERSION,$$CERT_MGR_VERSION,$$RUKPAK_VERSION,$$MANIFEST' < scripts/install.tpl.sh > install.sh ##@ Deployment @@ -159,7 +159,7 @@ endif install: export MANIFEST="./operator-controller.yaml" install: manifests kustomize generate ## Install CRDs into the K8s cluster specified in ~/.kube/config. kubectl kustomize config/default > operator-controller.yaml - envsubst '$$OLM_V0_VERSION,$$CERT_MGR_VERSION,$$RUKPAK_VERSION,$$MANIFEST' < scripts/install.tpl.sh | bash -s + envsubst '$$CATALOGD_VERSION,$$CERT_MGR_VERSION,$$RUKPAK_VERSION,$$MANIFEST' < scripts/install.tpl.sh | bash -s .PHONY: uninstall uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 5e5fc54fa..63e792ef4 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -6,30 +6,29 @@ metadata: name: manager-role rules: - apiGroups: - - "" + - catalogd.operatorframework.io resources: - - events + - bundlemetadata verbs: - - create - - patch + - list + - watch - apiGroups: - - core.rukpak.io + - catalogd.operatorframework.io resources: - - bundledeployments + - packages verbs: - - create - - get - list - - patch - - update - watch - apiGroups: - - operators.coreos.com + - core.rukpak.io resources: - - catalogsources + - bundledeployments verbs: + - create - get - list + - patch + - update - watch - apiGroups: - operators.operatorframework.io diff --git a/config/samples/catalogd_operatorcatalog.yaml b/config/samples/catalogd_operatorcatalog.yaml new file mode 100644 index 000000000..adb086b1d --- /dev/null +++ b/config/samples/catalogd_operatorcatalog.yaml @@ -0,0 +1,6 @@ +apiVersion: catalogd.operatorframework.io/v1beta1 +kind: CatalogSource +metadata: + name: operatorhubio +spec: + image: quay.io/operatorhubio/catalog:latest diff --git a/config/samples/operators_v1alpha1_operator.yaml b/config/samples/operators_v1alpha1_operator.yaml index e7720ffe4..46312e957 100644 --- a/config/samples/operators_v1alpha1_operator.yaml +++ b/config/samples/operators_v1alpha1_operator.yaml @@ -9,5 +9,4 @@ metadata: app.kubernetes.io/created-by: operator-controller name: operator-sample spec: - # TODO(user): Add fields here - packageName: prometheus + packageName: argocd-operator 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..59e531e3e 100644 --- a/controllers/operator_controller.go +++ b/controllers/operator_controller.go @@ -55,15 +55,9 @@ type OperatorReconciler struct { //+kubebuilder:rbac:groups=core.rukpak.io,resources=bundledeployments,verbs=get;list;watch;create;update;patch -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the Operator object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.1/pkg/reconcile +//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=bundlemetadata,verbs=list;watch +//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=packages,verbs=list;watch + func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { l := log.FromContext(ctx).WithName("operator-controller") l.V(1).Info("starting") @@ -121,7 +115,6 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha }) return ctrl.Result{}, nil } - // run resolution solution, err := r.Resolver.Resolve(ctx) if err != nil { diff --git a/controllers/operator_controller_test.go b/controllers/operator_controller_test.go index 5e7b27c15..c3f4c9c3d 100644 --- a/controllers/operator_controller_test.go +++ b/controllers/operator_controller_test.go @@ -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..c8d28c5aa 100644 --- a/go.mod +++ b/go.mod @@ -6,15 +6,11 @@ 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/catalogd v0.1.3 github.com/operator-framework/deppy v0.0.0-20230125110717-dc02e928470f - github.com/operator-framework/operator-registry v1.26.2 + github.com/operator-framework/operator-registry v1.26.3 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 @@ -43,9 +39,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect 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/google/uuid v1.3.0 // 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 @@ -54,6 +48,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/operator-framework/api v0.17.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -63,7 +58,8 @@ require ( 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/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect @@ -71,14 +67,17 @@ 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/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 + sigs.k8s.io/apiserver-runtime v1.1.2-0.20221226021050-33c901856927 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index 04378985e..5f63999a4 100644 --- a/go.sum +++ b/go.sum @@ -33,12 +33,13 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 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/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= 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= @@ -47,6 +48,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -56,11 +58,8 @@ 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/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= 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,17 +70,15 @@ 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= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= 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= @@ -99,6 +96,7 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -147,6 +145,7 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -158,7 +157,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,22 +174,20 @@ 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/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.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/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= 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= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -242,10 +238,12 @@ 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/catalogd v0.1.3 h1:prQNmmQw2FEWO4631aSvTGMrFm1/pH9x+lM9f7KU6VE= +github.com/operator-framework/catalogd v0.1.3/go.mod h1:CPnh6GWNwX5OTTeqnqjHFDXBGx1ZKmQ2v/4Q3bXN2qo= 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= -github.com/operator-framework/operator-registry v1.26.2/go.mod h1:Z7XIb/3ZkhBQCvMD/rJphyuY4LmU/eWpZS+o0Mm1WAk= +github.com/operator-framework/operator-registry v1.26.3 h1:U+HTGgjAT5RCXU2WkDwa525wcqdo97BsO7WfMhwL5MA= +github.com/operator-framework/operator-registry v1.26.3/go.mod h1:DZcTzhAyZf/NLi2UwBQA1F/qh4FwgYBcgBx2yBz8I+Q= github.com/operator-framework/rukpak v0.12.0 h1:qzM18RQZdqS00/6GLkEiTZf0pHtQ5bVg/DLzAmho+jQ= github.com/operator-framework/rukpak v0.12.0/go.mod h1:evm7OWW3t0A0PcVUpAzhJrhv4A9GXURfynGloAtyJ5A= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -280,15 +278,16 @@ 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/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 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 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -303,12 +302,27 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c h1:/RwRVN9EdXAVtdHxP7Ndn/tfmM9/goiwU0QTnLBgS4w= +go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= +go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8= +go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 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.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0 h1:xFSRQBbXF6VvYRf2lqMJXxoB72XI1K/azav8TekHHSw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.0 h1:Ajldaqhxqw/gNzQA45IKFWLdG7jZuXX/wBW1d5qvbUI= +go.opentelemetry.io/otel v1.10.0 h1:Y7DTJMR6zs1xkS/upamJYk0SxxN4C9AqRd77jmZnyY4= +go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0 h1:TaB+1rQhddO1sF71MpZOZAuSPW1klK2M8XxfrBMfK7Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0 h1:pDDYmo0QadUPal5fwXoY1pmMpFcdyhXOmL5drCrI3vU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0 h1:KtiUEhQmj/Pa874bVYKGNVdq8NPKiacPbaRRtgXi+t4= +go.opentelemetry.io/otel/metric v0.31.0 h1:6SiklT+gfWAwWUR0meEMxQBtihpiEs4c+vL9spDTqUs= +go.opentelemetry.io/otel/sdk v1.10.0 h1:jZ6K7sVn04kk/3DNUdJ4mqRlGDiXAVuIG+MMENpTNdY= +go.opentelemetry.io/otel/trace v1.10.0 h1:npQMbR8o7mum8uF95yFbOEJffhs1sbCOfDh8zAJiH5E= +go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= 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= @@ -324,6 +338,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -383,7 +398,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= @@ -395,8 +409,9 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -407,6 +422,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -440,11 +456,8 @@ 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= @@ -462,7 +475,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 +582,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 +589,7 @@ 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/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= 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 +602,7 @@ 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 +615,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= @@ -622,9 +626,9 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 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= @@ -650,12 +654,15 @@ k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66b k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc= +k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg= k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kms v0.26.1 h1:JE0n4J4+8/Z+egvXz2BTJeJ9ecsm4ZSLKF7ttVXXm/4= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= @@ -663,6 +670,9 @@ k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35 h1:+xBL5uTc+BkPBwmMi3vYfUJjq+N3K+H6PXeETwf5cPI= +sigs.k8s.io/apiserver-runtime v1.1.2-0.20221226021050-33c901856927 h1:YLqdUEPYZ5jzmORSZeRs5AZWFRJbZ0jrLivh3/X145M= +sigs.k8s.io/apiserver-runtime v1.1.2-0.20221226021050-33c901856927/go.mod h1:4MJyV8pBRl+X7ml5z/D5H0AVJyz9qb49MIt0295HWNA= sigs.k8s.io/controller-runtime v0.14.4 h1:Kd/Qgx5pd2XUL08eOV2vwIq3L9GhIbJ5Nxengbd4/0M= sigs.k8s.io/controller-runtime v0.14.4/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= 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/entity_sources/hardcoded/bundle_cache.go b/internal/resolution/entity_sources/hardcoded/bundle_cache.go deleted file mode 100644 index ae01a7a28..000000000 --- a/internal/resolution/entity_sources/hardcoded/bundle_cache.go +++ /dev/null @@ -1,57 +0,0 @@ -package hardcoded - -import ( - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" -) - -var HardcodedEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{ - "operatorhub/prometheus/0.14.0": *input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:c78cc60ad05445f423c66e37c464bc9f520f0c0741cfd351b4f839ae0b99bd4b"`, - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.14.0\"}", - }), - "operatorhub/prometheus/0.15.0": *input.NewEntity("operatorhub/prometheus/0.15.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:0a5a122cef6fabebcb82122bb4f5c4fbfa205454d109987b8af71672c8ac5c0e"`, - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.14.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.15.0\"}", - }), - "operatorhub/prometheus/0.22.2": *input.NewEntity("operatorhub/prometheus/0.22.2", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:f24b92e70ffb3bf33cc2a142f8b7a6519d28c90aa5742ddd37ac4fcecb5e5a52"`, - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.15.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.22.2\"}", - }), - "operatorhub/prometheus/0.27.0": *input.NewEntity("operatorhub/prometheus/0.27.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:7aace7b24fa2587c61d37d8676e23ea24dce03f1751b94614c6af60fba364f63"`, - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.22.2\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.27.0\"}", - }), - "operatorhub/prometheus/0.32.0": *input.NewEntity("operatorhub/prometheus/0.32.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:14f75077f01feab351f7a046ccfcff6ad357bed2393048d0f6a41f6e64c63278"`, - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.27.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PodMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PodMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.32.0\"}", - }), - "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.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.32.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PodMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PodMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ThanosRuler\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ThanosRuler\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.37.0\"}", - }), - "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.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.37.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"AlertmanagerConfig\",\"version\":\"v1alpha1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"AlertmanagerConfig\",\"version\":\"v1alpha1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PodMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PodMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Probe\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Probe\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ThanosRuler\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ThanosRuler\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.47.0\"}", - }), - "operatorhub/lightbend-console-operator/0.0.1": *input.NewEntity("operatorhub/lightbend-console-operator/0.0.1", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/lightbend-console-operator@sha256:2cf5f1abf71be29b7d2667ae9ca4102198c93cdef450d09faf1b26900443e285"`, - "olm.channel": "{\"channelName\":\"alpha\",\"priority\":0}", - "olm.gvk": "[{\"group\":\"app.lightbend.com\",\"kind\":\"Console\",\"version\":\"v1alpha1\"}]", - "olm.package": "{\"packageName\":\"lightbend-console-operator\",\"version\":\"0.0.1\"}", - }), -}) diff --git a/internal/resolution/entitysources/catalogdsource.go b/internal/resolution/entitysources/catalogdsource.go new file mode 100644 index 000000000..f706d5d5d --- /dev/null +++ b/internal/resolution/entitysources/catalogdsource.go @@ -0,0 +1,129 @@ +package entitysources + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/operator-framework/deppy/pkg/deppy" + "github.com/operator-framework/deppy/pkg/deppy/input" + "github.com/operator-framework/operator-registry/alpha/property" + "sigs.k8s.io/controller-runtime/pkg/client" + + catalogd "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1" +) + +// catalogdEntitySource is a source for(/collection of) deppy defined input.Entity, built from content +// made accessible on-cluster by https://github.com/operator-framework/catalogd. +// It is an implementation of deppy defined input.EntitySource +type catalogdEntitySource struct { + client client.Client +} + +func NewCatalogdEntitySource(client client.Client) *catalogdEntitySource { + + return &catalogdEntitySource{client: client} +} + +func (es *catalogdEntitySource) Get(ctx context.Context, id deppy.Identifier) *input.Entity { + panic("not implemented") +} + +func (es *catalogdEntitySource) Filter(ctx context.Context, filter input.Predicate) (input.EntityList, error) { + resultSet := input.EntityList{} + entities, err := getEntities(ctx, es.client) + if err != nil { + return nil, err + } + for _, entity := range entities { + if filter(&entity) { + resultSet = append(resultSet, entity) + } + } + return resultSet, nil +} + +func (es *catalogdEntitySource) GroupBy(ctx context.Context, fn input.GroupByFunction) (input.EntityListMap, error) { + entities, err := getEntities(ctx, es.client) + if err != nil { + return nil, err + } + resultSet := input.EntityListMap{} + for _, entity := range entities { + keys := fn(&entity) + for _, key := range keys { + resultSet[key] = append(resultSet[key], entity) + } + } + return resultSet, nil +} + +func (es *catalogdEntitySource) Iterate(ctx context.Context, fn input.IteratorFunction) error { + entities, err := getEntities(ctx, es.client) + if err != nil { + return err + } + for _, entity := range entities { + if err := fn(&entity); err != nil { + return err + } + } + return nil +} + +func getEntities(ctx context.Context, client client.Client) (input.EntityList, error) { + entities := input.EntityList{} + bundleMetadatas, packageMetdatas, err := fetchMetadata(ctx, client) + if err != nil { + return nil, err + } + for _, bundle := range bundleMetadatas.Items { + props := map[string]string{} + + for _, prop := range bundle.Spec.Properties { + switch prop.Type { + case property.TypePackage: + // this is already a json marshalled object, so it doesn't need to be marshalled + // like the other ones + props[property.TypePackage] = string(prop.Value) + } + } + + imgValue, err := json.Marshal(bundle.Spec.Image) + if err != nil { + return nil, err + } + props["olm.bundle.path"] = string(imgValue) + bundlePkg := packageMetdatas[bundle.Spec.Package] + for _, ch := range bundlePkg.Spec.Channels { + for _, b := range ch.Entries { + if b.Name == bundle.Name { + channelValue, _ := json.Marshal(property.Channel{ChannelName: ch.Name, Priority: 0}) + props[property.TypeChannel] = string(channelValue) + entity := input.Entity{ + ID: deppy.IdentifierFromString(fmt.Sprintf("%s%s%s", bundle.Name, bundle.Spec.Package, ch.Name)), + Properties: props, + } + entities = append(entities, entity) + } + } + } + } + return entities, nil +} + +func fetchMetadata(ctx context.Context, client client.Client) (catalogd.BundleMetadataList, map[string]catalogd.Package, error) { + packageMetdatas := catalogd.PackageList{} + if err := client.List(ctx, &packageMetdatas); err != nil { + return catalogd.BundleMetadataList{}, nil, err + } + bundleMetadatas := catalogd.BundleMetadataList{} + if err := client.List(ctx, &bundleMetadatas); err != nil { + return catalogd.BundleMetadataList{}, nil, err + } + packages := map[string]catalogd.Package{} + for _, pkg := range packageMetdatas.Items { + packages[pkg.Name] = pkg + } + return bundleMetadatas, packages, nil +} 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..7eb9df662 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" @@ -31,9 +30,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" + catalogd "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1" operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/controllers" "github.com/operator-framework/operator-controller/internal/resolution" + "github.com/operator-framework/operator-controller/internal/resolution/entitysources" ) var ( @@ -43,12 +44,12 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme)) utilruntime.Must(rukpakv1alpha1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme + utilruntime.Must(catalogd.AddToScheme(scheme)) - // required by the catalog source cache controller - utilruntime.Must(olmv0v1alpha1.AddToScheme(scheme)) + //+kubebuilder:scaffold:scheme } func main() { @@ -92,20 +93,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(), entitysources.NewCatalogdEntitySource(mgr.GetClient())), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Operator") os.Exit(1) diff --git a/scripts/install.tpl.sh b/scripts/install.tpl.sh index 3fd173e12..07f236379 100644 --- a/scripts/install.tpl.sh +++ b/scripts/install.tpl.sh @@ -7,14 +7,14 @@ if [[ -z "$operator_controller_manifest" ]]; then exit 1 fi -olm_version=$OLM_V0_VERSION +catalogd_version=$CATALOGD_VERSION cert_mgr_version=$CERT_MGR_VERSION rukpak_version=$RUKPAK_VERSION -if [[ -z "$olm_version" || -z "$cert_mgr_version" || -z "$rukpak_version" ]]; then +if [[ -z "$catalogd_version" || -z "$cert_mgr_version" || -z "$rukpak_version" ]]; then err="Error: Missing component version(s) for: " - if [[ -z "$olm_version" ]]; then - err+="operator-lifecycle-manager " + if [[ -z "$catalogd_version" ]]; then + err+="catalogd " fi if [[ -z "$cert_mgr_version" ]]; then err+="cert-manager " @@ -34,8 +34,6 @@ function kubectl_wait() { kubectl wait --for=condition=Available --namespace="${namespace}" "${runtime}" --timeout="${timeout}" } -curl -L -s "https://github.com/operator-framework/operator-lifecycle-manager/releases/download/${olm_version}/install.sh" | bash -s "${olm_version}" - kubectl apply -f "https://github.com/cert-manager/cert-manager/releases/download/${cert_mgr_version}/cert-manager.yaml" kubectl_wait "cert-manager" "deployment/cert-manager-webhook" "60s" @@ -44,5 +42,8 @@ kubectl_wait "rukpak-system" "deployment/core" "60s" kubectl_wait "rukpak-system" "deployment/helm-provisioner" "60s" kubectl_wait "rukpak-system" "deployment/rukpak-webhooks" "60s" +kubectl apply -f https://github.com/operator-framework/catalogd/releases/download/${catalogd_version}/catalogd.yaml +kubectl_wait "catalogd-system" "deployment/catalogd-controller-manager" "60s" + kubectl apply -f "${operator_controller_manifest}" kubectl_wait "operator-controller-system" "deployment/operator-controller-controller-manager" "60s" 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..e05e8ff2f 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -6,13 +6,12 @@ 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" + catalogd "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1" operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" ) @@ -39,12 +38,8 @@ 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()) - + err = catalogd.AddToScheme(scheme) + Expect(err).ToNot(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..a6a12ed4d 100644 --- a/test/e2e/install_test.go +++ b/test/e2e/install_test.go @@ -11,6 +11,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + catalogd "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1" operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" ) @@ -22,67 +23,73 @@ const ( var _ = Describe("Operator Install", func() { var ( - ctx context.Context - pkgName string - operatorName string - operator *operatorv1alpha1.Operator + ctx context.Context + pkgName string + operatorName string + operator *operatorv1alpha1.Operator + operatorCatalog *catalogd.CatalogSource ) + When("An operator is installed from an operator catalog", func() { + BeforeEach(func() { + ctx = context.Background() + pkgName = "argocd-operator" + operatorName = fmt.Sprintf("operator-%s", rand.String(8)) + operator = &operatorv1alpha1.Operator{ + ObjectMeta: metav1.ObjectMeta{ + Name: operatorName, + }, + Spec: operatorv1alpha1.OperatorSpec{ + PackageName: pkgName, + }, + } + operatorCatalog = &catalogd.CatalogSource{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-catalog", + }, + Spec: catalogd.CatalogSourceSpec{ + // (TODO): Set up a local image registry, and build and store a test catalog in it + // to use in the test suite + Image: "quay.io/operatorhubio/catalog:latest", + }, + } + err := c.Create(ctx, operatorCatalog) + Expect(err).ToNot(HaveOccurred()) + Eventually(func(g Gomega) { + err = c.Get(ctx, types.NamespacedName{Name: "test-catalog"}, operatorCatalog) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(len(operatorCatalog.Status.Conditions)).To(Equal(1)) + g.Expect(operatorCatalog.Status.Conditions[0].Message).To(Equal("catalog contents have been unpacked and are available on cluster")) + }).WithTimeout(5 * time.Minute).WithPolling(defaultPoll).Should(Succeed()) + }) + It("resolves the specified package with correct bundle path", func() { + By("creating the Operator resource") + err := c.Create(ctx, operator) + Expect(err).ToNot(HaveOccurred()) - var testNamespace string - cleanup := func() {} - ctx = context.TODO() - BeforeEach(func() { - testNamespace = createTestNamespace(ctx, c, "registry-grpc-") - cleanup = applyCRDifNotPresent(ctx) - testPrefix := "registry-grpc-" + By("eventually reporting a successful resolution and bundle path") + Eventually(func(g Gomega) { + err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(len(operator.Status.Conditions)).To(Equal(1)) + g.Expect(operator.Status.Conditions[0].Message).To(Equal("install was successful")) + }).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed()) - 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) + By("eventually installing the package successfully") + Eventually(func(g Gomega) { + bd := rukpakv1alpha1.BundleDeployment{} + err = c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(len(bd.Status.Conditions)).To(Equal(2)) + g.Expect(bd.Status.Conditions[0].Reason).To(Equal("UnpackSuccessful")) + g.Expect(bd.Status.Conditions[1].Reason).To(Equal("InstallationSucceeded")) + }).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed()) - }) - AfterEach(func() { - deleteTestNamespace(ctx, c, testNamespace) - cleanup() - }) - It("resolves the specified package with correct bundle path", func() { - ctx = context.Background() - pkgName = "project-quay" - operatorName = fmt.Sprintf("operator-%s", rand.String(8)) - operator = &operatorv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{ - Name: operatorName, - }, - Spec: operatorv1alpha1.OperatorSpec{ - PackageName: pkgName, - }, - } - - By("creating the Operator resource") - err := c.Create(ctx, operator) - Expect(err).ToNot(HaveOccurred()) - - By("eventually reporting a successful resolution and bundle path") - Eventually(func(g Gomega) { - err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(len(operator.Status.Conditions)).To(Equal(1)) - g.Expect(operator.Status.Conditions[0].Message).To(Equal("install was successful")) - }).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed()) - - By("eventually installing the package successfully") - Eventually(func(g Gomega) { - bd := rukpakv1alpha1.BundleDeployment{} - err = c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(len(bd.Status.Conditions)).To(Equal(2)) - g.Expect(bd.Status.Conditions[0].Reason).To(Equal("UnpackSuccessful")) - g.Expect(bd.Status.Conditions[1].Reason).To(Equal("InstallationSucceeded")) - }).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed()) - - By("deleting the Operator resource") - err = c.Delete(ctx, operator) - Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + err := c.Delete(ctx, operatorCatalog) + Expect(err).ToNot(HaveOccurred()) + err = c.Delete(ctx, operator) + Expect(err).ToNot(HaveOccurred()) + }) }) }) 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: {}