From ad615069ee422d5a0974f81738979e07ec5b5225 Mon Sep 17 00:00:00 2001 From: Varsha Prasad Narsing Date: Thu, 22 Jun 2023 14:54:55 -0400 Subject: [PATCH] [BD_annotations] Add relevant Operator level annotations on BD This commit makes Operator controller to add annotations on BundleDeployments realted to operator version and channel. Will be useful for runtime based resolutions. Signed-off-by: Varsha Prasad Narsing --- internal/controllers/operator_controller.go | 51 +++++++++++++++++-- .../controllers/operator_controller_test.go | 15 ++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/internal/controllers/operator_controller.go b/internal/controllers/operator_controller.go index 5d626d54c..c35013118 100644 --- a/internal/controllers/operator_controller.go +++ b/internal/controllers/operator_controller.go @@ -52,6 +52,13 @@ type OperatorReconciler struct { Resolver *solver.DeppySolver } +// bundleDeploymentMetadata serves as an extensible struct holding +// any metadata that is to be added to bundleDeployments. +type bundleDeploymentMetadata struct { + channel string + version string +} + //+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators,verbs=get;list;watch //+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators/status,verbs=get;update;patch //+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators/finalizers,verbs=update @@ -73,7 +80,7 @@ func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } reconciledOp := existingOp.DeepCopy() - res, reconcileErr := r.reconcile(ctx, reconciledOp) + res, reconcileErr := r.reconcile(ctx, reconciledOp, l) // Do checks before any Update()s, as Update() may modify the resource structure! updateStatus := !equality.Semantic.DeepEqual(existingOp.Status, reconciledOp.Status) @@ -113,7 +120,7 @@ func checkForUnexpectedFieldChange(a, b operatorsv1alpha1.Operator) bool { // to return different results (e.g. requeue). // //nolint:unparam -func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha1.Operator) (ctrl.Result, error) { +func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha1.Operator, log logr.Logger) (ctrl.Result, error) { // validate spec if err := validators.ValidateOperatorSpec(op); err != nil { // Set the TypeInstalled condition to Unknown to indicate that the resolution @@ -172,9 +179,17 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration()) return ctrl.Result{}, err } + + // Get annotations which needs to be added to the bundleDeployment. + // An error here need not re-trigger a reconcile. + bundleDeploymentAnnotations := &bundleDeploymentMetadata{} + if err = bundleDeploymentAnnotations.CompleteBundleDeploymentMetadata(bundleEntity); err != nil { + log.Error(err, "unable to fetch annotations from the resolved bundleEntity") + } + // Ensure a BundleDeployment exists with its bundle source from the bundle // image we just looked up in the solution. - dep := r.generateExpectedBundleDeployment(*op, bundleImage, bundleProvisioner) + dep := r.generateExpectedBundleDeployment(*op, bundleImage, bundleProvisioner, bundleDeploymentAnnotations) if err := r.ensureBundleDeployment(ctx, dep); err != nil { // originally Reason: operatorsv1alpha1.ReasonInstallationFailed op.Status.InstalledBundleResource = "" @@ -260,7 +275,7 @@ func (r *OperatorReconciler) getBundleEntityFromSolution(solution *solver.Soluti return nil, fmt.Errorf("entity for package %q not found in solution", packageName) } -func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string, bundleProvisioner string) *unstructured.Unstructured { +func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string, bundleProvisioner string, annotations *bundleDeploymentMetadata) *unstructured.Unstructured { // We use unstructured here to avoid problems of serializing default values when sending patches to the apiserver. // If you use a typed object, any default values from that struct get serialized into the JSON patch, which could // cause unrelated fields to be patched back to the default value even though that isn't the intention. Using an @@ -272,6 +287,10 @@ func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha "kind": rukpakv1alpha1.BundleDeploymentKind, "metadata": map[string]interface{}{ "name": o.GetName(), + "annotations": map[string]string{ + "operator_version": annotations.version, + "operator_channel": annotations.channel, + }, }, "spec": map[string]interface{}{ // TODO: Don't assume plain provisioner @@ -459,3 +478,27 @@ func operatorRequestsForCatalog(ctx context.Context, c client.Reader, logger log return requests } } + +// Complete fills in the annotations from the information received from +// bundleEntities. +func (bdm *bundleDeploymentMetadata) CompleteBundleDeploymentMetadata(entity *entity.BundleEntity) error { + var errs []error + + channel, err := entity.ChannelName() + if err != nil || channel == "" { + errs = append(errs, fmt.Errorf("unable to find the channel name from resolved entity: %v", err)) + channel = "unknown" + } + bdm.channel = channel + + var version string + semverVer, err := entity.Version() + version = semverVer.String() + if err != nil || version == "" { + errs = append(errs, fmt.Errorf("unable to find the version of operator to be installed from resolved entity: %v", err)) + version = "unknown" + } + bdm.version = version + + return utilerrors.NewAggregate(errs) +} diff --git a/internal/controllers/operator_controller_test.go b/internal/controllers/operator_controller_test.go index 8cf7b1d5f..00ec2ec0d 100644 --- a/internal/controllers/operator_controller_test.go +++ b/internal/controllers/operator_controller_test.go @@ -162,6 +162,9 @@ var _ = Describe("Operator Controller Test", func() { Expect(bd.Spec.Template.Spec.Source.Type).To(Equal(rukpakv1alpha1.SourceTypeImage)) Expect(bd.Spec.Template.Spec.Source.Image).NotTo(BeNil()) Expect(bd.Spec.Template.Spec.Source.Image.Ref).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + + By("verifying annotations") + verifyBundleDeploymentAnnotations(bd) }) It("sets the resolvedBundleResource status field", func() { Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) @@ -243,6 +246,9 @@ var _ = Describe("Operator Controller Test", func() { Expect(bd.Spec.Template.Spec.Source.Image).NotTo(BeNil()) Expect(bd.Spec.Template.Spec.Source.Image.Ref).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + By("verifying annotations") + verifyBundleDeploymentAnnotations(bd) + By("Checking the status fields") Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) Expect(operator.Status.InstalledBundleResource).To(Equal("")) @@ -555,6 +561,9 @@ var _ = Describe("Operator Controller Test", func() { Expect(bd.Spec.Template.Spec.Source.Type).To(Equal(rukpakv1alpha1.SourceTypeImage)) Expect(bd.Spec.Template.Spec.Source.Image).NotTo(BeNil()) Expect(bd.Spec.Template.Spec.Source.Image.Ref).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) + + By("verifying annotations") + verifyBundleDeploymentAnnotations(bd) }) It("sets the resolvedBundleResource status field", func() { Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed")) @@ -1081,6 +1090,12 @@ func verifyConditionsInvariants(op *operatorsv1alpha1.Operator) { } } +func verifyBundleDeploymentAnnotations(bd *rukpakv1alpha1.BundleDeployment) { + annotations := bd.GetAnnotations() + Expect(annotations["operator_channel"]).To(BeEquivalentTo("beta")) + Expect(annotations["operator_version"]).To(BeEquivalentTo("0.47.0")) +} + 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"`,