From 3e561f6b242d3eb54d7be7742d3f3f117b5505a0 Mon Sep 17 00:00:00 2001 From: Melissa Lee Date: Mon, 6 Feb 2023 06:25:01 -0800 Subject: [PATCH] Share controller code further --- common/types.go | 1 + controllers/runtimecomponent_controller.go | 261 ++------------ utils/update.go | 375 +++++++++++++++++++++ 3 files changed, 401 insertions(+), 236 deletions(-) create mode 100644 utils/update.go diff --git a/common/types.go b/common/types.go index 2235880b4..04a625ead 100644 --- a/common/types.go +++ b/common/types.go @@ -129,6 +129,7 @@ type BaseComponentService interface { type BaseComponentNetworkPolicy interface { GetNamespaceLabels() map[string]string GetFromLabels() map[string]string + IsDisabled() bool } // BaseComponentMonitoring represents basic service monitoring configuration diff --git a/controllers/runtimecomponent_controller.go b/controllers/runtimecomponent_controller.go index 0bf070018..55417cd75 100644 --- a/controllers/runtimecomponent_controller.go +++ b/controllers/runtimecomponent_controller.go @@ -20,7 +20,6 @@ import ( "context" "fmt" "os" - "strings" "github.com/application-stacks/runtime-component-operator/common" "github.com/pkg/errors" @@ -50,7 +49,6 @@ import ( networkingv1 "k8s.io/api/networking/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" servingv1 "knative.dev/serving/pkg/apis/serving/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -87,7 +85,7 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req ns, err := appstacksutils.GetOperatorNamespace() // When running the operator locally, `ns` will be empty string - if ns == "" { + if err != nil || ns == "" { // Since this method can be called directly from unit test, populate `watchNamespaces`. if r.watchNamespaces == nil { r.watchNamespaces, err = appstacksutils.GetWatchNamespaces() @@ -122,7 +120,7 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req // Fetch the RuntimeComponent instance instance := &appstacksv1beta2.RuntimeComponent{} - var ba common.BaseComponent = instance + // var ba common.BaseComponent = instance err = r.GetClient().Get(context.TODO(), req.NamespacedName, instance) if err != nil { if kerrors.IsNotFound(err) { @@ -167,57 +165,13 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req Namespace: instance.Namespace, } - imageReferenceOld := instance.Status.ImageReference - instance.Status.ImageReference = instance.Spec.ApplicationImage - if r.IsOpenShift() { - image, err := imageutil.ParseDockerImageReference(instance.Spec.ApplicationImage) - if err == nil { - isTag := &imagev1.ImageStreamTag{} - isTagName := imageutil.JoinImageStreamTag(image.Name, image.Tag) - isTagNamespace := image.Namespace - if isTagNamespace == "" { - isTagNamespace = instance.Namespace - } - key := types.NamespacedName{Name: isTagName, Namespace: isTagNamespace} - err = r.GetAPIReader().Get(context.Background(), key, isTag) - // Call ManageError only if the error type is not found or is not forbidden. Forbidden could happen - // when the operator tries to call GET for ImageStreamTags on a namespace that doesn't exists (e.g. - // cannot get imagestreamtags.image.openshift.io in the namespace "navidsh": no RBAC policy matched) - if err == nil { - image := isTag.Image - if image.DockerImageReference != "" { - instance.Status.ImageReference = image.DockerImageReference - } - } else if err != nil && !kerrors.IsNotFound(err) && !kerrors.IsForbidden(err) && !strings.Contains(isTagName, "/") { - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - } - } - if imageReferenceOld != instance.Status.ImageReference { - reqLogger.Info("Updating status.imageReference", "status.imageReference", instance.Status.ImageReference) - err = r.UpdateStatus(instance) - if err != nil { - reqLogger.Error(err, "Error updating RuntimeComponent status") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } + if err = r.UpdateImageReference(instance); err != nil { + reqLogger.Error(err, "Error updating RuntimeComponent") + return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } - if instance.Spec.ServiceAccountName == nil || *instance.Spec.ServiceAccountName == "" { - serviceAccount := &corev1.ServiceAccount{ObjectMeta: defaultMeta} - err = r.CreateOrUpdate(serviceAccount, instance, func() error { - return appstacksutils.CustomizeServiceAccount(serviceAccount, instance, r.GetClient()) - }) - if err != nil { - reqLogger.Error(err, "Failed to reconcile ServiceAccount") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - } else { - serviceAccount := &corev1.ServiceAccount{ObjectMeta: defaultMeta} - err = r.DeleteResource(serviceAccount) - if err != nil { - reqLogger.Error(err, "Failed to delete ServiceAccount") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } + if err = r.UpdateServiceAccount(instance, defaultMeta); err != nil { + return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } // Check if the ServiceAccount has a valid pull secret before creating the deployment/statefulset @@ -235,44 +189,10 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req } if instance.Spec.CreateKnativeService != nil && *instance.Spec.CreateKnativeService { - // Clean up non-Knative resources - resources := []client.Object{ - &corev1.Service{ObjectMeta: defaultMeta}, - &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: instance.Name + "-headless", Namespace: instance.Namespace}}, - &appsv1.Deployment{ObjectMeta: defaultMeta}, - &appsv1.StatefulSet{ObjectMeta: defaultMeta}, - &autoscalingv1.HorizontalPodAutoscaler{ObjectMeta: defaultMeta}, - } - err = r.DeleteResources(resources) + err = r.UpdateKnativeService(instance, defaultMeta, isKnativeSupported) if err != nil { - reqLogger.Error(err, "Failed to clean up non-Knative resources") return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - - if ok, _ := r.IsGroupVersionSupported(networkingv1.SchemeGroupVersion.String(), "Ingress"); ok { - r.DeleteResource(&networkingv1.Ingress{ObjectMeta: defaultMeta}) - } - - if r.IsOpenShift() { - route := &routev1.Route{ObjectMeta: defaultMeta} - err = r.DeleteResource(route) - if err != nil { - reqLogger.Error(err, "Failed to clean up non-Knative resource Route") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - } - - if isKnativeSupported { - ksvc := &servingv1.Service{ObjectMeta: defaultMeta} - err = r.CreateOrUpdate(ksvc, instance, func() error { - appstacksutils.CustomizeKnativeService(ksvc, instance) - return nil - }) - - if err != nil { - reqLogger.Error(err, "Failed to reconcile Knative Service") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } + } else { return r.ManageSuccess(common.StatusConditionTypeReconciled, instance) } return r.ManageError(errors.New("failed to reconcile Knative service as operator could not find Knative CRDs"), common.StatusConditionTypeReconciled, instance) @@ -287,75 +207,25 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req } } - useCertmanager, err := r.GenerateSvcCertSecret(ba, "rco", "Runtime Component Operator", "runtime-component-operator") + useCertmanager, err := r.UpdateSvcCertSecret(instance, "rco", "Runtime Component Operator", "runtime-component-operator") if err != nil { - reqLogger.Error(err, "Failed to reconcile CertManager Certificate") return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } - if ba.GetService().GetCertificateSecretRef() != nil { - ba.GetStatus().SetReference(common.StatusReferenceCertSecretName, *ba.GetService().GetCertificateSecretRef()) - } - svc := &corev1.Service{ObjectMeta: defaultMeta} - err = r.CreateOrUpdate(svc, instance, func() error { - appstacksutils.CustomizeService(svc, ba) - svc.Annotations = appstacksutils.MergeMaps(svc.Annotations, instance.Spec.Service.Annotations) - if !useCertmanager && r.IsOpenShift() { - appstacksutils.AddOCPCertAnnotation(ba, svc) - } - monitoringEnabledLabelName := getMonitoringEnabledLabelName(ba) - if instance.Spec.Monitoring != nil { - svc.Labels[monitoringEnabledLabelName] = "true" - } else { - delete(svc.Labels, monitoringEnabledLabelName) - } - return nil - }) - if err != nil { - reqLogger.Error(err, "Failed to reconcile Service") + if err = r.UpdateService(instance, defaultMeta, useCertmanager); err != nil { return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } - networkPolicy := &networkingv1.NetworkPolicy{ObjectMeta: defaultMeta} - if np := instance.Spec.NetworkPolicy; !np.IsDisabled() { - err = r.CreateOrUpdate(networkPolicy, instance, func() error { - appstacksutils.CustomizeNetworkPolicy(networkPolicy, r.IsOpenShift(), instance) - return nil - }) - if err != nil { - reqLogger.Error(err, "Failed to reconcile network policy") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - } else { - if err := r.DeleteResource(networkPolicy); err != nil { - reqLogger.Error(err, "Failed to delete network policy") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } + if err = r.UpdateNetworkPolicy(instance, defaultMeta); err != nil { + return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } - err = r.ReconcileBindings(instance) - if err != nil { - return r.ManageError(err, common.StatusConditionTypeReconciled, ba) + if err = r.ReconcileBindings(instance); err != nil { + return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } if instance.Spec.StatefulSet != nil { - // Delete Deployment if exists - deploy := &appsv1.Deployment{ObjectMeta: defaultMeta} - err = r.DeleteResource(deploy) - - if err != nil { - reqLogger.Error(err, "Failed to delete Deployment") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - svc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: instance.Name + "-headless", Namespace: instance.Namespace}} - err = r.CreateOrUpdate(svc, instance, func() error { - appstacksutils.CustomizeService(svc, instance) - svc.Spec.ClusterIP = corev1.ClusterIPNone - svc.Spec.Type = corev1.ServiceTypeClusterIP - return nil - }) - if err != nil { - reqLogger.Error(err, "Failed to reconcile headless Service") + if err = r.UpdateStatefulSetReq(instance, defaultMeta); err != nil { return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } @@ -375,22 +245,10 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req } } else { - // Delete StatefulSet if exists - statefulSet := &appsv1.StatefulSet{ObjectMeta: defaultMeta} - err = r.DeleteResource(statefulSet) - if err != nil { - reqLogger.Error(err, "Failed to delete Statefulset") + if err = r.UpdateDeploymentReq(instance, defaultMeta); err != nil { return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } - // Delete StatefulSet if exists - headlesssvc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: instance.Name + "-headless", Namespace: instance.Namespace}} - err = r.DeleteResource(headlesssvc) - - if err != nil { - reqLogger.Error(err, "Failed to delete headless Service") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } deploy := &appsv1.Deployment{ObjectMeta: defaultMeta} err = r.CreateOrUpdate(deploy, instance, func() error { appstacksutils.CustomizeDeployment(deploy, instance) @@ -404,79 +262,26 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req reqLogger.Error(err, "Failed to reconcile Deployment") return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } - } - if instance.Spec.Autoscaling != nil { - hpa := &autoscalingv1.HorizontalPodAutoscaler{ObjectMeta: defaultMeta} - err = r.CreateOrUpdate(hpa, instance, func() error { - appstacksutils.CustomizeHPA(hpa, instance) - return nil - }) - - if err != nil { - reqLogger.Error(err, "Failed to reconcile HorizontalPodAutoscaler") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - } else { - hpa := &autoscalingv1.HorizontalPodAutoscaler{ObjectMeta: defaultMeta} - err = r.DeleteResource(hpa) - if err != nil { - reqLogger.Error(err, "Failed to delete HorizontalPodAutoscaler") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } + if err = r.UpdateAutoscaling(instance, defaultMeta); err != nil { + return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } if ok, err := r.IsGroupVersionSupported(routev1.SchemeGroupVersion.String(), "Route"); err != nil { reqLogger.Error(err, fmt.Sprintf("Failed to check if %s is supported", routev1.SchemeGroupVersion.String())) r.ManageError(err, common.StatusConditionTypeReconciled, instance) } else if ok { - if instance.Spec.Expose != nil && *instance.Spec.Expose { - route := &routev1.Route{ObjectMeta: defaultMeta} - err = r.CreateOrUpdate(route, instance, func() error { - key, cert, caCert, destCACert, err := r.GetRouteTLSValues(ba) - if err != nil { - return err - } - appstacksutils.CustomizeRoute(route, ba, key, cert, caCert, destCACert) - - return nil - }) - if err != nil { - reqLogger.Error(err, "Failed to reconcile Route") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - } else { - route := &routev1.Route{ObjectMeta: defaultMeta} - err = r.DeleteResource(route) - if err != nil { - reqLogger.Error(err, "Failed to delete Route") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } + if err = r.UpdateRoute(instance, defaultMeta); err != nil { + return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } } else { - if ok, err := r.IsGroupVersionSupported(networkingv1.SchemeGroupVersion.String(), "Ingress"); err != nil { reqLogger.Error(err, fmt.Sprintf("Failed to check if %s is supported", networkingv1.SchemeGroupVersion.String())) r.ManageError(err, common.StatusConditionTypeReconciled, instance) } else if ok { - if instance.Spec.Expose != nil && *instance.Spec.Expose { - ing := &networkingv1.Ingress{ObjectMeta: defaultMeta} - err = r.CreateOrUpdate(ing, instance, func() error { - appstacksutils.CustomizeIngress(ing, instance) - return nil - }) - if err != nil { - reqLogger.Error(err, "Failed to reconcile Ingress") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - } else { - ing := &networkingv1.Ingress{ObjectMeta: defaultMeta} - err = r.DeleteResource(ing) - if err != nil { - reqLogger.Error(err, "Failed to delete Ingress") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } + if err = r.UpdateIngress(instance, defaultMeta); err != nil { + return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } } } @@ -485,25 +290,9 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req reqLogger.Error(err, fmt.Sprintf("Failed to check if %s is supported", prometheusv1.SchemeGroupVersion.String())) r.ManageError(err, common.StatusConditionTypeReconciled, instance) } else if ok { - if instance.Spec.Monitoring != nil && (instance.Spec.CreateKnativeService == nil || !*instance.Spec.CreateKnativeService) { - sm := &prometheusv1.ServiceMonitor{ObjectMeta: defaultMeta} - err = r.CreateOrUpdate(sm, instance, func() error { - appstacksutils.CustomizeServiceMonitor(sm, instance) - return nil - }) - if err != nil { - reqLogger.Error(err, "Failed to reconcile ServiceMonitor") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } - } else { - sm := &prometheusv1.ServiceMonitor{ObjectMeta: defaultMeta} - err = r.DeleteResource(sm) - if err != nil { - reqLogger.Error(err, "Failed to delete ServiceMonitor") - return r.ManageError(err, common.StatusConditionTypeReconciled, instance) - } + if err = r.UpdateServiceMonitor(instance, defaultMeta); err != nil { + return r.ManageError(err, common.StatusConditionTypeReconciled, instance) } - } else { reqLogger.V(1).Info(fmt.Sprintf("%s is not supported", prometheusv1.SchemeGroupVersion.String())) } diff --git a/utils/update.go b/utils/update.go new file mode 100644 index 000000000..84283f06b --- /dev/null +++ b/utils/update.go @@ -0,0 +1,375 @@ +package utils + +import ( + "context" + "errors" + "strings" + + "github.com/application-stacks/runtime-component-operator/common" + imagev1 "github.com/openshift/api/image/v1" + routev1 "github.com/openshift/api/route/v1" + "github.com/openshift/library-go/pkg/image/imageutil" + prometheusv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + appsv1 "k8s.io/api/apps/v1" + autoscalingv1 "k8s.io/api/autoscaling/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + servingv1 "knative.dev/serving/pkg/apis/serving/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// UpdateImageStreamTag updates image reference +func (r *ReconcilerBase) UpdateImageReference(ba common.BaseComponent) error { + status := ba.GetStatus() + metaObj := ba.(metav1.Object) + + imageReferenceOld := status.GetImageReference() + status.SetImageReference(ba.GetApplicationImage()) + + if r.IsOpenShift() { + image, err := imageutil.ParseDockerImageReference(ba.GetApplicationImage()) + if err == nil { + isTag := &imagev1.ImageStreamTag{} + isTagName := imageutil.JoinImageStreamTag(image.Name, image.Tag) + isTagNamespace := image.Namespace + if isTagNamespace == "" { + isTagNamespace = metaObj.GetNamespace() + } + key := types.NamespacedName{Name: isTagName, Namespace: isTagNamespace} + err = r.GetAPIReader().Get(context.Background(), key, isTag) + // Call ManageError only if the error type is not found or is not forbidden. Forbidden could happen + // when the operator tries to call GET for ImageStreamTags on a namespace that doesn't exists (e.g. + // cannot get imagestreamtags.image.openshift.io in the namespace "navidsh": no RBAC policy matched) + if err == nil { + image := isTag.Image + if image.DockerImageReference != "" { + status.SetImageReference(image.DockerImageReference) + } + } else if err != nil && !kerrors.IsNotFound(err) && !kerrors.IsForbidden(err) && !strings.Contains(isTagName, "/") { + return err + } + } + } + + if imageReferenceOld != status.GetImageReference() { + log.Info("Updating status.imageReference", "status.imageReference", status.GetImageReference()) + err := r.UpdateStatus(ba.(client.Object)) + if err != nil { + return err + } + } + + return nil +} + +// UpdateServiceAccount creates or updates service account +func (r *ReconcilerBase) UpdateServiceAccount(ba common.BaseComponent, defaultMeta metav1.ObjectMeta) error { + metaObj := ba.(metav1.Object) + + if ba.GetServiceAccountName() == nil || *ba.GetServiceAccountName() == "" { + serviceAccount := &corev1.ServiceAccount{ObjectMeta: defaultMeta} + err := r.CreateOrUpdate(serviceAccount, metaObj, func() error { + return CustomizeServiceAccount(serviceAccount, ba, r.GetClient()) + }) + if err != nil { + log.Error(err, "Failed to reconcile ServiceAccount") + return err + } + } else { + serviceAccount := &corev1.ServiceAccount{ObjectMeta: defaultMeta} + err := r.DeleteResource(serviceAccount) + if err != nil { + log.Error(err, "Failed to delete ServiceAccount") + return err + } + } + + // Check if the ServiceAccount has a valid pull secret before creating the deployment/statefulset + // or setting up knative. Otherwise the pods can go into an ImagePullBackOff loop + saErr := ServiceAccountPullSecretExists(ba, r.GetClient()) + if saErr != nil { + return saErr + } + + return nil +} + +// UpdateServiceAccount creates or updates service account +func (r *ReconcilerBase) UpdateKnativeService(ba common.BaseComponent, defaultMeta metav1.ObjectMeta, isKnativeSupported bool) error { + metaObj := ba.(metav1.Object) + + // Clean up non-Knative resources + resources := []client.Object{ + &corev1.Service{ObjectMeta: defaultMeta}, + &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: metaObj.GetName() + "-headless", Namespace: metaObj.GetNamespace()}}, + &appsv1.Deployment{ObjectMeta: defaultMeta}, + &appsv1.StatefulSet{ObjectMeta: defaultMeta}, + &autoscalingv1.HorizontalPodAutoscaler{ObjectMeta: defaultMeta}, + } + err := r.DeleteResources(resources) + if err != nil { + log.Error(err, "Failed to clean up non-Knative resources") + return err + } + + if ok, _ := r.IsGroupVersionSupported(networkingv1.SchemeGroupVersion.String(), "Ingress"); ok { + r.DeleteResource(&networkingv1.Ingress{ObjectMeta: defaultMeta}) + } + + if r.IsOpenShift() { + route := &routev1.Route{ObjectMeta: defaultMeta} + err = r.DeleteResource(route) + if err != nil { + log.Error(err, "Failed to clean up non-Knative resource Route") + return err + } + } + + if isKnativeSupported { + ksvc := &servingv1.Service{ObjectMeta: defaultMeta} + err = r.CreateOrUpdate(ksvc, metaObj, func() error { + CustomizeKnativeService(ksvc, ba) + return nil + }) + + if err != nil { + log.Error(err, "Failed to reconcile Knative Service") + + return err + } + return nil + } + + return errors.New("failed to reconcile Knative service as operator could not find Knative CRDs") +} + +// UpdateSvcCertSecret creates or updates service cert secret +func (r *ReconcilerBase) UpdateSvcCertSecret(ba common.BaseComponent, prefix string, CACommonName string, operatorName string) (bool, error) { + useCertmanager, err := r.GenerateSvcCertSecret(ba, prefix, CACommonName, operatorName) + if err != nil { + log.Error(err, "Failed to reconcile CertManager Certificate") + return useCertmanager, err + } + if ba.GetService().GetCertificateSecretRef() != nil { + ba.GetStatus().SetReference(common.StatusReferenceCertSecretName, *ba.GetService().GetCertificateSecretRef()) + } + + return useCertmanager, nil +} + +// UpdateService creates or updates service +func (r *ReconcilerBase) UpdateService(ba common.BaseComponent, defaultMeta metav1.ObjectMeta, useCertmanager bool) error { + metaObj := ba.(metav1.Object) + + svc := &corev1.Service{ObjectMeta: defaultMeta} + err := r.CreateOrUpdate(svc, metaObj, func() error { + CustomizeService(svc, ba) + svc.Annotations = MergeMaps(svc.Annotations, ba.GetAnnotations()) + if !useCertmanager && r.IsOpenShift() { + AddOCPCertAnnotation(ba, svc) + } + monitoringEnabledLabelName := getMonitoringEnabledLabelName(ba) + if ba.GetMonitoring() != nil { + svc.Labels[monitoringEnabledLabelName] = "true" + } else { + delete(svc.Labels, monitoringEnabledLabelName) + } + return nil + }) + if err != nil { + log.Error(err, "Failed to reconcile Service") + return err + } + + return nil +} + +// UpdateNetworkPolicy creates or updates network policy +func (r *ReconcilerBase) UpdateNetworkPolicy(ba common.BaseComponent, defaultMeta metav1.ObjectMeta) error { + metaObj := ba.(metav1.Object) + + networkPolicy := &networkingv1.NetworkPolicy{ObjectMeta: defaultMeta} + if np := ba.GetNetworkPolicy(); !np.IsDisabled() { + err := r.CreateOrUpdate(networkPolicy, metaObj, func() error { + CustomizeNetworkPolicy(networkPolicy, r.IsOpenShift(), ba) + return nil + }) + if err != nil { + log.Error(err, "Failed to reconcile network policy") + return err + } + } else { + if err := r.DeleteResource(networkPolicy); err != nil { + log.Error(err, "Failed to delete network policy") + return err + } + } + + return nil +} + +// UpdateStatefulSetReq fulfills prerequesites for statefulset +func (r *ReconcilerBase) UpdateStatefulSetReq(ba common.BaseComponent, defaultMeta metav1.ObjectMeta) error { + metaObj := ba.(metav1.Object) + + // Delete Deployment if exists + deploy := &appsv1.Deployment{ObjectMeta: defaultMeta} + err := r.DeleteResource(deploy) + + if err != nil { + log.Error(err, "Failed to delete Deployment") + return err + } + svc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: metaObj.GetName() + "-headless", Namespace: metaObj.GetNamespace()}} + err = r.CreateOrUpdate(svc, metaObj, func() error { + CustomizeService(svc, ba) + svc.Spec.ClusterIP = corev1.ClusterIPNone + svc.Spec.Type = corev1.ServiceTypeClusterIP + return nil + }) + if err != nil { + log.Error(err, "Failed to reconcile headless Service") + return err + } + return nil +} + +// UpdateDeploymentReq fulfills prerequesites for deployment +func (r *ReconcilerBase) UpdateDeploymentReq(ba common.BaseComponent, defaultMeta metav1.ObjectMeta) error { + metaObj := ba.(metav1.Object) + + // Delete StatefulSet if exists + statefulSet := &appsv1.StatefulSet{ObjectMeta: defaultMeta} + err := r.DeleteResource(statefulSet) + if err != nil { + log.Error(err, "Failed to delete Statefulset") + return err + } + + // Delete StatefulSet if exists + headlesssvc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: metaObj.GetName() + "-headless", Namespace: metaObj.GetNamespace()}} + err = r.DeleteResource(headlesssvc) + + if err != nil { + log.Error(err, "Failed to delete headless Service") + return err + } + + return nil +} + +// UpdateAutoscaling creates or updates HPA +func (r *ReconcilerBase) UpdateAutoscaling(ba common.BaseComponent, defaultMeta metav1.ObjectMeta) error { + metaObj := ba.(metav1.Object) + + if ba.GetAutoscaling() != nil { + hpa := &autoscalingv1.HorizontalPodAutoscaler{ObjectMeta: defaultMeta} + err := r.CreateOrUpdate(hpa, metaObj, func() error { + CustomizeHPA(hpa, ba) + return nil + }) + + if err != nil { + log.Error(err, "Failed to reconcile HorizontalPodAutoscaler") + return err + } + } else { + hpa := &autoscalingv1.HorizontalPodAutoscaler{ObjectMeta: defaultMeta} + err := r.DeleteResource(hpa) + if err != nil { + log.Error(err, "Failed to delete HorizontalPodAutoscaler") + return err + } + } + return nil +} + +// UpdateRoute creates or updates route +func (r *ReconcilerBase) UpdateRoute(ba common.BaseComponent, defaultMeta metav1.ObjectMeta) error { + metaObj := ba.(metav1.Object) + + if ba.GetExpose() != nil && *ba.GetExpose() { + route := &routev1.Route{ObjectMeta: defaultMeta} + err := r.CreateOrUpdate(route, metaObj, func() error { + key, cert, caCert, destCACert, err := r.GetRouteTLSValues(ba) + if err != nil { + return err + } + CustomizeRoute(route, ba, key, cert, caCert, destCACert) + + return nil + }) + if err != nil { + log.Error(err, "Failed to reconcile Route") + return err + } + } else { + route := &routev1.Route{ObjectMeta: defaultMeta} + err := r.DeleteResource(route) + if err != nil { + log.Error(err, "Failed to delete Route") + return err + } + } + + return nil +} + +// UpdateIngress creates or updates ingress +func (r *ReconcilerBase) UpdateIngress(ba common.BaseComponent, defaultMeta metav1.ObjectMeta) error { + metaObj := ba.(metav1.Object) + + if ba.GetExpose() != nil && *ba.GetExpose() { + ing := &networkingv1.Ingress{ObjectMeta: defaultMeta} + err := r.CreateOrUpdate(ing, metaObj, func() error { + CustomizeIngress(ing, ba) + return nil + }) + if err != nil { + log.Error(err, "Failed to reconcile Ingress") + return err + } + } else { + ing := &networkingv1.Ingress{ObjectMeta: defaultMeta} + err := r.DeleteResource(ing) + if err != nil { + log.Error(err, "Failed to delete Ingress") + return err + } + } + + return nil +} + +// UpdateIngress creates or updates service monitor +func (r *ReconcilerBase) UpdateServiceMonitor(ba common.BaseComponent, defaultMeta metav1.ObjectMeta) error { + metaObj := ba.(metav1.Object) + + if ba.GetMonitoring() != nil && (ba.GetCreateKnativeService() == nil || !*ba.GetCreateKnativeService()) { + sm := &prometheusv1.ServiceMonitor{ObjectMeta: defaultMeta} + err := r.CreateOrUpdate(sm, metaObj, func() error { + CustomizeServiceMonitor(sm, ba) + return nil + }) + if err != nil { + log.Error(err, "Failed to reconcile ServiceMonitor") + return err + } + } else { + sm := &prometheusv1.ServiceMonitor{ObjectMeta: defaultMeta} + err := r.DeleteResource(sm) + if err != nil { + log.Error(err, "Failed to delete ServiceMonitor") + return err + } + } + + return nil +} + +func getMonitoringEnabledLabelName(ba common.BaseComponent) string { + return "monitor." + ba.GetGroupName() + "/enabled" +}