diff --git a/api/v1alpha1/operator_types.go b/api/v1alpha1/operator_types.go index b0612c21d..2ff203275 100644 --- a/api/v1alpha1/operator_types.go +++ b/api/v1alpha1/operator_types.go @@ -32,8 +32,9 @@ const ( // TODO(user): add more Types, here and into init() TypeReady = "Ready" - // TODO(user): add more Reasons, here and into init() - ReasonNotImplemented = "NotImplemented" + ReasonNotImplemented = "NotImplemented" + ReasonResolutionFailed = "ResolutionFailed" + ReasonResolutionSucceeded = "ResolutionSucceeded" ) func init() { @@ -54,6 +55,7 @@ type OperatorStatus struct { // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + BundlePath string `json:"BundlePath,omitempty"` } //+kubebuilder:object:root=true diff --git a/config/crd/bases/operators.operatorframework.io_operators.yaml b/config/crd/bases/operators.operatorframework.io_operators.yaml index 894b7699c..595ce2adb 100644 --- a/config/crd/bases/operators.operatorframework.io_operators.yaml +++ b/config/crd/bases/operators.operatorframework.io_operators.yaml @@ -45,6 +45,8 @@ spec: status: description: OperatorStatus defines the observed state of Operator properties: + BundlePath: + type: string conditions: items: description: "Condition contains details for one aspect of the current diff --git a/controllers/operator_controller.go b/controllers/operator_controller.go index 63905b37f..d3e5de8d0 100644 --- a/controllers/operator_controller.go +++ b/controllers/operator_controller.go @@ -18,7 +18,10 @@ package controllers import ( "context" + "strings" + operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + "github.com/operator-framework/operator-controller/internal/resolution" "k8s.io/apimachinery/pkg/api/equality" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -27,14 +30,14 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) // OperatorReconciler reconciles a Operator object type OperatorReconciler struct { client.Client Scheme *runtime.Scheme + + resolver *resolution.OperatorResolver } //+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators,verbs=get;list;watch;create;update;patch;delete @@ -97,24 +100,60 @@ func checkForUnexpectedFieldChange(a, b operatorsv1alpha1.Operator) bool { // Helper function to do the actual reconcile func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha1.Operator) (ctrl.Result, error) { - // TODO(user): change ReasonNotImplemented when functionality added - readyCondition := metav1.Condition{ - Type: operatorsv1alpha1.TypeReady, - Status: metav1.ConditionFalse, - Reason: operatorsv1alpha1.ReasonNotImplemented, - Message: "The Reconcile operation is not implemented", - ObservedGeneration: op.GetGeneration(), + // todo(perdasilva): this is a _hack_ we probably want to find a better way to ride or die resolve and update + solution, err := r.resolver.Resolve(ctx) + status := metav1.ConditionTrue + reason := operatorsv1alpha1.ReasonResolutionSucceeded + message := "resolution was successful" + if err != nil { + status = metav1.ConditionTrue + reason = operatorsv1alpha1.ReasonResolutionFailed + message = err.Error() } - apimeta.SetStatusCondition(&op.Status.Conditions, readyCondition) - // TODO(user): your logic here + // todo(perdasilva): more hacks - need to fix up the solution structure to be more useful + packageVariableIDMap := map[string]string{} + for variableID, ok := range solution { + if ok { + idComponents := strings.Split(string(variableID), "/") + packageVariableIDMap[idComponents[1]] = string(variableID) + } + } + + operatorList := &operatorsv1alpha1.OperatorList{} + if err := r.Client.List(ctx, operatorList); err != nil { + return ctrl.Result{}, err + } + + for _, operator := range operatorList.Items { + apimeta.SetStatusCondition(&operator.Status.Conditions, metav1.Condition{ + Type: operatorsv1alpha1.TypeReady, + Status: status, + Reason: reason, + Message: message, + ObservedGeneration: op.GetGeneration(), + }) + if varID, ok := packageVariableIDMap[operator.Spec.PackageName]; ok { + operator.Status.BundlePath = varID + } + if err := r.Client.Status().Update(ctx, &operator); err != nil { + return ctrl.Result{}, err + } + } return ctrl.Result{}, nil } // SetupWithManager sets up the controller with the Manager. func (r *OperatorReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). + r.resolver = resolution.NewOperatorResolver(mgr.GetClient(), resolution.HardcodedEntitySource) + + err := ctrl.NewControllerManagedBy(mgr). For(&operatorsv1alpha1.Operator{}). Complete(r) + + if err != nil { + return err + } + return nil } diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 1265c0ad4..c613c3fa3 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -21,38 +21,39 @@ import ( "fmt" "path/filepath" "testing" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - + operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + "github.com/operator-framework/operator-controller/controllers" + operatorutil "github.com/operator-framework/operator-controller/internal/util" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/rand" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" - "github.com/operator-framework/operator-controller/controllers" - operatorutil "github.com/operator-framework/operator-controller/internal/util" //+kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment +var ( + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment + ctx context.Context + cancel context.CancelFunc +) func TestAPIs(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Controller Suite") } @@ -80,15 +81,39 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + err = (&controllers.OperatorReconciler{ + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + ctx, cancel = context.WithCancel(context.Background()) + go func() { + defer GinkgoRecover() + err = k8sManager.Start(ctx) + Expect(err).ToNot(HaveOccurred(), "failed to run manager") + }() + }) var _ = AfterSuite(func() { + cancel() By("tearing down the test environment") err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) var _ = Describe("Reconcile Test", func() { + const ( + timeout = time.Second * 10 + interval = time.Millisecond * 250 + ) + When("an Operator is created", func() { var ( operator *operatorsv1alpha1.Operator @@ -99,10 +124,8 @@ var _ = Describe("Reconcile Test", func() { ) BeforeEach(func() { ctx = context.Background() - opName = fmt.Sprintf("operator-test-%s", rand.String(8)) pkgName = fmt.Sprintf("package-test-%s", rand.String(8)) - operator = &operatorsv1alpha1.Operator{ ObjectMeta: metav1.ObjectMeta{ Name: opName, @@ -113,17 +136,6 @@ var _ = Describe("Reconcile Test", func() { } err = k8sClient.Create(ctx, operator) Expect(err).To(Not(HaveOccurred())) - - or := controllers.OperatorReconciler{ - k8sClient, - scheme.Scheme, - } - _, err = or.Reconcile(ctx, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: opName, - }, - }) - Expect(err).To(Not(HaveOccurred())) }) AfterEach(func() { err = k8sClient.Delete(ctx, operator) @@ -131,11 +143,14 @@ var _ = Describe("Reconcile Test", func() { }) It("has all Conditions created", func() { op := &operatorsv1alpha1.Operator{} - - err = k8sClient.Get(ctx, client.ObjectKey{ - Name: opName, - }, op) - Expect(err).To(Not(HaveOccurred())) + opLookupKey := client.ObjectKey{Name: opName} + Eventually(func() bool { + err := k8sClient.Get(ctx, opLookupKey, op) + if err != nil { + return false + } + return len(op.Status.Conditions) > 0 + }, timeout, interval).Should(BeTrue()) // All defined condition Types MUST exist after reconciliation conds := op.Status.Conditions diff --git a/internal/resolution/bundle_cache.go b/internal/resolution/bundle_cache.go new file mode 100644 index 000000000..1992e19b9 --- /dev/null +++ b/internal/resolution/bundle_cache.go @@ -0,0 +1,51 @@ +package resolution + +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\"}", + }), +})