From 34d3e5555b9de3c90508acfbf548cf07c8c1c65e Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Mon, 25 May 2026 16:27:02 +0530 Subject: [PATCH 1/8] remove openshift CRD as hard dependency Signed-off-by: Anand Kumar Singh --- cmd/main.go | 68 ++++++++++++++++---- controllers/gitopsservice_controller.go | 35 ++++++---- controllers/gitopsservice_controller_test.go | 4 ++ controllers/util/util.go | 23 +++++++ controllers/util/util_test.go | 34 ++++++++++ 5 files changed, 140 insertions(+), 24 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 32bbb1b4427..ae4ea62ae56 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -133,7 +133,7 @@ func main() { ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) if err := util.InspectCluster(); err != nil { - setupLog.Info("unable to inspect cluster") + setupLog.Error(err, "unable to inspect cluster") } disableHTTP2 := func(c *tls.Config) { @@ -198,18 +198,56 @@ func main() { client = mgr.GetClient() } - registerComponentOrExit(mgr, console.AddToScheme) - registerComponentOrExit(mgr, routev1.AddToScheme) // Adding the routev1 api + // Setup Scheme for OpenShift Console if available (verified by InspectCluster) + if util.IsConsoleAPIFound() { + registerComponentOrExit(mgr, console.AddToScheme) + } + + // Setup Scheme for OpenShift Route if available (verified by InspectCluster) + if util.IsRouteAPIFound() { + registerComponentOrExit(mgr, routev1.AddToScheme) + } + registerComponentOrExit(mgr, operatorsv1.AddToScheme) registerComponentOrExit(mgr, operatorsv1alpha1.AddToScheme) registerComponentOrExit(mgr, argov1alpha1api.AddToScheme) registerComponentOrExit(mgr, argov1beta1api.AddToScheme) - registerComponentOrExit(mgr, configv1.AddToScheme) + + // Setup Scheme for OpenShift Config if available + if found, err := argoutil.VerifyAPI(configv1.GroupName, configv1.GroupVersion.Version); err != nil { + setupLog.Error(err, "unable to verify Config API") + os.Exit(1) + } else if found { + registerComponentOrExit(mgr, configv1.AddToScheme) + } + registerComponentOrExit(mgr, monitoringv1.AddToScheme) registerComponentOrExit(mgr, rolloutManagerApi.AddToScheme) - registerComponentOrExit(mgr, templatev1.AddToScheme) - registerComponentOrExit(mgr, appsv1.AddToScheme) - registerComponentOrExit(mgr, oauthv1.AddToScheme) + + // Setup Scheme for OpenShift Template if available + if found, err := argoutil.VerifyAPI(templatev1.GroupName, templatev1.GroupVersion.Version); err != nil { + setupLog.Error(err, "unable to verify Template API") + os.Exit(1) + } else if found { + registerComponentOrExit(mgr, templatev1.AddToScheme) + } + + // Setup Scheme for OpenShift Apps if available + if found, err := argoutil.VerifyAPI(appsv1.GroupName, appsv1.GroupVersion.Version); err != nil { + setupLog.Error(err, "unable to verify Apps API") + os.Exit(1) + } else if found { + registerComponentOrExit(mgr, appsv1.AddToScheme) + } + + // Setup Scheme for OpenShift OAuth if available + if found, err := argoutil.VerifyAPI(oauthv1.GroupName, oauthv1.GroupVersion.Version); err != nil { + setupLog.Error(err, "unable to verify OAuth API") + os.Exit(1) + } else if found { + registerComponentOrExit(mgr, oauthv1.AddToScheme) + } + registerComponentOrExit(mgr, crdv1.AddToScheme) // Start webhook only if ENABLE_CONVERSION_WEBHOOK is set @@ -229,12 +267,16 @@ func main() { os.Exit(1) } - if err = (&controllers.ReconcileArgoCDRoute{ - Client: client, - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Argo CD route") - os.Exit(1) + if util.IsRouteAPIFound() { + if err = (&controllers.ReconcileArgoCDRoute{ + Client: client, + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Argo CD route") + os.Exit(1) + } + } else { + setupLog.Info("Route API not found, skipping ReconcileArgoCDRoute controller setup") } if err = (&controllers.ArgoCDMetricsReconciler{ diff --git a/controllers/gitopsservice_controller.go b/controllers/gitopsservice_controller.go index bf8d602bb9c..ace208c3c2e 100644 --- a/controllers/gitopsservice_controller.go +++ b/controllers/gitopsservice_controller.go @@ -27,6 +27,7 @@ import ( argoapp "github.com/argoproj-labs/argocd-operator/api/v1beta1" argocommon "github.com/argoproj-labs/argocd-operator/common" argocdcontroller "github.com/argoproj-labs/argocd-operator/controllers/argocd" + argoutil "github.com/argoproj-labs/argocd-operator/controllers/argocd" argocdutil "github.com/argoproj-labs/argocd-operator/controllers/argoutil" "github.com/go-logr/logr" version "github.com/hashicorp/go-version" @@ -101,15 +102,20 @@ func (r *ReconcileGitopsService) SetupWithManager(mgr ctrl.Manager) error { reqLogger.Error(err, "Failed to create GitOps service instance") } - return ctrl.NewControllerManagedBy(mgr). + bldr := ctrl.NewControllerManagedBy(mgr). For(&pipelinesv1alpha1.GitopsService{}, builder.WithPredicates(pred)). Owns(&rbacv1.ClusterRoleBinding{}). Owns(&rbacv1.ClusterRole{}). Owns(&corev1.ServiceAccount{}). Owns(&corev1.ConfigMap{}). Owns(&appsv1.Deployment{}, builder.WithPredicates(pred)). - Owns(&corev1.Service{}, builder.WithPredicates(pred)). - Owns(&routev1.Route{}, builder.WithPredicates(pred)). + Owns(&corev1.Service{}, builder.WithPredicates(pred)) + + if util.IsRouteAPIFound() { + bldr = bldr.Owns(&routev1.Route{}, builder.WithPredicates(pred)) + } + + return bldr. Watches( &corev1.Namespace{}, &handler.EnqueueRequestForObject{}, @@ -216,7 +222,12 @@ func (r *ReconcileGitopsService) Reconcile(ctx context.Context, request reconcil reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) reqLogger.Info("Reconciling GitopsService") - // Fetch the GitopsService instance + // Fetch the GitopsService instance if on OpenShift cluster + if !argoutil.IsOpenShiftCluster() { + reqLogger.Info("Skip GitopsService reconcile: Not an OpenShift cluster") + return reconcile.Result{}, nil + } + instance := &pipelinesv1alpha1.GitopsService{} err := r.Client.Get(ctx, types.NamespacedName{Name: serviceName}, instance) if err != nil { @@ -344,14 +355,16 @@ func (r *ReconcileGitopsService) cleanKAMResources(ctx context.Context, reqLogge } // KAM Route - cleanupKAMRoute := &routev1.Route{} - if err := r.Client.Get(ctx, types.NamespacedName{Name: kamResourceName, Namespace: serviceNamespace}, cleanupKAMRoute); err == nil { - reqLogger.Info("Detected unsupported KAM Route, deleting", "Name", kamResourceName, "Namespace", serviceNamespace) - if err := r.Client.Delete(ctx, cleanupKAMRoute); err != nil && !errors.IsNotFound(err) { - reqLogger.Error(err, "Failed to delete KAM Route", "Name", kamResourceName, "Namespace", serviceNamespace) + if util.IsRouteAPIFound() { + cleanupKAMRoute := &routev1.Route{} + if err := r.Client.Get(ctx, types.NamespacedName{Name: kamResourceName, Namespace: serviceNamespace}, cleanupKAMRoute); err == nil { + reqLogger.Info("Detected unsupported KAM Route, deleting", "Name", kamResourceName, "Namespace", serviceNamespace) + if err := r.Client.Delete(ctx, cleanupKAMRoute); err != nil && !errors.IsNotFound(err) { + reqLogger.Error(err, "Failed to delete KAM Route", "Name", kamResourceName, "Namespace", serviceNamespace) + } + } else if !errors.IsNotFound(err) { + reqLogger.Error(err, "Failed to retrieve KAM Route", "Name", kamResourceName, "Namespace", serviceNamespace) } - } else if !errors.IsNotFound(err) { - reqLogger.Error(err, "Failed to retrieve KAM Route", "Name", kamResourceName, "Namespace", serviceNamespace) } } diff --git a/controllers/gitopsservice_controller_test.go b/controllers/gitopsservice_controller_test.go index cfc7c6be07c..6c98d31c34c 100644 --- a/controllers/gitopsservice_controller_test.go +++ b/controllers/gitopsservice_controller_test.go @@ -927,6 +927,8 @@ func TestCleanKAMResources_ServiceExist(t *testing.T) { // Route exist func TestCleanKAMResources_RouteExist(t *testing.T) { logf.SetLogger(zap.New(zap.UseDevMode(true))) + defer util.SetRouteAPIFound(util.IsRouteAPIFound()) + util.SetRouteAPIFound(true) s := scheme.Scheme addKnownTypesToScheme(s) kamRoute := &routev1.Route{ @@ -947,6 +949,8 @@ func TestCleanKAMResources_RouteExist(t *testing.T) { // All Resources exist func TestCleanKAMResources_AllResourcesExist(t *testing.T) { logf.SetLogger(zap.New(zap.UseDevMode(true))) + defer util.SetRouteAPIFound(util.IsRouteAPIFound()) + util.SetRouteAPIFound(true) s := scheme.Scheme addKnownTypesToScheme(s) kamDeploy := &appsv1.Deployment{ diff --git a/controllers/util/util.go b/controllers/util/util.go index 50fe4c23869..1664945c2fb 100644 --- a/controllers/util/util.go +++ b/controllers/util/util.go @@ -25,6 +25,7 @@ import ( "github.com/argoproj-labs/argocd-operator/controllers/argoutil" configv1 "github.com/openshift/api/config/v1" console "github.com/openshift/api/console/v1" + routev1 "github.com/openshift/api/route/v1" "golang.org/x/mod/semver" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -39,6 +40,7 @@ const ( var ( consoleAPIFound = false + routeAPIFound = false ) // GetClusterVersion returns the OpenShift Cluster version in which the operator is installed @@ -75,6 +77,9 @@ func InspectCluster() error { if err := verifyConsoleAPI(); err != nil { return err } + if err := verifyRouteAPI(); err != nil { + return err + } return nil } @@ -96,6 +101,24 @@ func verifyConsoleAPI() error { return nil } +func IsRouteAPIFound() bool { + return routeAPIFound +} + +// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** +func SetRouteAPIFound(found bool) { + routeAPIFound = found +} + +func verifyRouteAPI() error { + found, err := argoutil.VerifyAPI(routev1.GroupName, routev1.GroupVersion.Version) + if err != nil { + return err + } + routeAPIFound = found + return nil +} + func ProxyEnvVars(vars ...corev1.EnvVar) []corev1.EnvVar { result := []corev1.EnvVar{} result = append(result, vars...) diff --git a/controllers/util/util_test.go b/controllers/util/util_test.go index 2a254894519..7b0c3a5ac31 100644 --- a/controllers/util/util_test.go +++ b/controllers/util/util_test.go @@ -52,6 +52,40 @@ func addKnownTypesToScheme(scheme *runtime.Scheme) { scheme.AddKnownTypes(configv1.GroupVersion, &configv1.ClusterVersion{}) } +func TestRouteAPIFound(t *testing.T) { + t.Run("Route API not found by default", func(t *testing.T) { + defer SetRouteAPIFound(false) + SetRouteAPIFound(false) + if IsRouteAPIFound() { + t.Fatal("expected Route API to not be found") + } + }) + t.Run("SetRouteAPIFound sets the value", func(t *testing.T) { + defer SetRouteAPIFound(false) + SetRouteAPIFound(true) + if !IsRouteAPIFound() { + t.Fatal("expected Route API to be found after SetRouteAPIFound(true)") + } + }) +} + +func TestConsoleAPIFound(t *testing.T) { + t.Run("Console API not found by default", func(t *testing.T) { + defer SetConsoleAPIFound(false) + SetConsoleAPIFound(false) + if IsConsoleAPIFound() { + t.Fatal("expected Console API to not be found") + } + }) + t.Run("SetConsoleAPIFound sets the value", func(t *testing.T) { + defer SetConsoleAPIFound(false) + SetConsoleAPIFound(true) + if !IsConsoleAPIFound() { + t.Fatal("expected Console API to be found after SetConsoleAPIFound(true)") + } + }) +} + func assertNoError(t *testing.T, err error) { t.Helper() if err != nil { From 2352bddcc3c97796f40f291e0850ef17856eea60 Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Mon, 25 May 2026 16:40:05 +0530 Subject: [PATCH 2/8] add a check for argocd-metrics to skip if monitoring api is not available Signed-off-by: Anand Kumar Singh --- cmd/main.go | 4 ++++ controllers/argocd_metrics_controller.go | 6 ++++++ controllers/util/util.go | 25 ++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index ae4ea62ae56..34460d48d47 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -208,6 +208,10 @@ func main() { registerComponentOrExit(mgr, routev1.AddToScheme) } + if util.IsMonitoringAPIFound() { + registerComponentOrExit(mgr, monitoringv1.AddToScheme) + } + registerComponentOrExit(mgr, operatorsv1.AddToScheme) registerComponentOrExit(mgr, operatorsv1alpha1.AddToScheme) registerComponentOrExit(mgr, argov1alpha1api.AddToScheme) diff --git a/controllers/argocd_metrics_controller.go b/controllers/argocd_metrics_controller.go index 0b352c61cb8..e5dc611703d 100644 --- a/controllers/argocd_metrics_controller.go +++ b/controllers/argocd_metrics_controller.go @@ -28,6 +28,7 @@ import ( argocdutil "github.com/argoproj-labs/argocd-operator/controllers/argoutil" "github.com/go-logr/logr" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "github.com/redhat-developer/gitops-operator/controllers/util" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -83,6 +84,11 @@ func (r *ArgoCDMetricsReconciler) Reconcile(ctx context.Context, request reconci reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) reqLogger.Info("Reconciling ArgoCD Metrics") + if !util.IsMonitoringAPIFound() { + reqLogger.V(1).Info("monitoring.coreos.com API not available, skipping metrics reconciliation") + return reconcile.Result{}, nil + } + namespace := corev1.Namespace{} err := r.Client.Get(ctx, types.NamespacedName{Name: request.Namespace}, &namespace) if err != nil { diff --git a/controllers/util/util.go b/controllers/util/util.go index 1664945c2fb..f06671ec860 100644 --- a/controllers/util/util.go +++ b/controllers/util/util.go @@ -26,6 +26,7 @@ import ( configv1 "github.com/openshift/api/config/v1" console "github.com/openshift/api/console/v1" routev1 "github.com/openshift/api/route/v1" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "golang.org/x/mod/semver" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -39,8 +40,9 @@ const ( ) var ( - consoleAPIFound = false - routeAPIFound = false + consoleAPIFound = false + routeAPIFound = false + monitoringAPIFound = false ) // GetClusterVersion returns the OpenShift Cluster version in which the operator is installed @@ -80,6 +82,9 @@ func InspectCluster() error { if err := verifyRouteAPI(); err != nil { return err } + if err := verifyMonitoringAPI(); err != nil { + return err + } return nil } @@ -119,6 +124,22 @@ func verifyRouteAPI() error { return nil } +func verifyMonitoringAPI() error { + found, err := argoutil.VerifyAPI( + monitoringv1.SchemeGroupVersion.Group, + monitoringv1.SchemeGroupVersion.Version, + ) + if err != nil { + return err + } + monitoringAPIFound = found + return nil +} + +func IsMonitoringAPIFound() bool { + return monitoringAPIFound +} + func ProxyEnvVars(vars ...corev1.EnvVar) []corev1.EnvVar { result := []corev1.EnvVar{} result = append(result, vars...) From ae67f3e9f96e9cf6d761c4f2669706d91d620824 Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Tue, 26 May 2026 15:40:17 +0530 Subject: [PATCH 3/8] fix failure and integrate review comment assisted-by: claude-code for code,planning remove duplicate import, add checks before gitopsService and other OCP specific controller Signed-off-by: Anand Kumar Singh --- cmd/main.go | 42 +++++++++++--------- controllers/argocd_metrics_controller.go | 6 --- controllers/gitopsservice_controller.go | 7 ---- controllers/gitopsservice_controller_test.go | 7 ++++ controllers/util/test_util.go | 36 +++++++++++++++++ controllers/util/util.go | 35 +++++++++------- controllers/util/util_test.go | 17 ++++++++ 7 files changed, 105 insertions(+), 45 deletions(-) create mode 100644 controllers/util/test_util.go diff --git a/cmd/main.go b/cmd/main.go index 34460d48d47..0aa0fd7cf40 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -208,6 +208,7 @@ func main() { registerComponentOrExit(mgr, routev1.AddToScheme) } + // Setup Scheme for Prometheus Operator if available (verified by InspectCluster) if util.IsMonitoringAPIFound() { registerComponentOrExit(mgr, monitoringv1.AddToScheme) } @@ -218,14 +219,10 @@ func main() { registerComponentOrExit(mgr, argov1beta1api.AddToScheme) // Setup Scheme for OpenShift Config if available - if found, err := argoutil.VerifyAPI(configv1.GroupName, configv1.GroupVersion.Version); err != nil { - setupLog.Error(err, "unable to verify Config API") - os.Exit(1) - } else if found { + if util.IsOpenShiftCluster() { registerComponentOrExit(mgr, configv1.AddToScheme) } - registerComponentOrExit(mgr, monitoringv1.AddToScheme) registerComponentOrExit(mgr, rolloutManagerApi.AddToScheme) // Setup Scheme for OpenShift Template if available @@ -262,13 +259,17 @@ func main() { } } - if err = (&controllers.ReconcileGitopsService{ - Client: client, - Scheme: mgr.GetScheme(), - DisableDefaultInstall: strings.ToLower(os.Getenv(common.DisableDefaultInstallEnvVar)) == "true", - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "GitopsService") - os.Exit(1) + if util.IsOpenShiftCluster() { + if err = (&controllers.ReconcileGitopsService{ + Client: client, + Scheme: mgr.GetScheme(), + DisableDefaultInstall: strings.ToLower(os.Getenv(common.DisableDefaultInstallEnvVar)) == "true", + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "GitopsService") + os.Exit(1) + } + } else { + setupLog.Info("OpenShift Cluster not found, skipping ReconcileGitopsService controller setup") } if util.IsRouteAPIFound() { @@ -283,13 +284,18 @@ func main() { setupLog.Info("Route API not found, skipping ReconcileArgoCDRoute controller setup") } - if err = (&controllers.ArgoCDMetricsReconciler{ - Client: client, - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Argo CD metrics") - os.Exit(1) + if util.IsMonitoringAPIFound() { + if err = (&controllers.ArgoCDMetricsReconciler{ + Client: client, + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Argo CD metrics") + os.Exit(1) + } + } else { + setupLog.Info("Monitoring API not found, skipping Argo CD metrics controller setup") } + // Check the label selector format eg. "foo=bar" if _, err := labels.Parse(labelSelectorFlag); err != nil { setupLog.Error(err, "error parsing the labelSelector '%s'.", labelSelectorFlag) diff --git a/controllers/argocd_metrics_controller.go b/controllers/argocd_metrics_controller.go index e5dc611703d..0b352c61cb8 100644 --- a/controllers/argocd_metrics_controller.go +++ b/controllers/argocd_metrics_controller.go @@ -28,7 +28,6 @@ import ( argocdutil "github.com/argoproj-labs/argocd-operator/controllers/argoutil" "github.com/go-logr/logr" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" - "github.com/redhat-developer/gitops-operator/controllers/util" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -84,11 +83,6 @@ func (r *ArgoCDMetricsReconciler) Reconcile(ctx context.Context, request reconci reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) reqLogger.Info("Reconciling ArgoCD Metrics") - if !util.IsMonitoringAPIFound() { - reqLogger.V(1).Info("monitoring.coreos.com API not available, skipping metrics reconciliation") - return reconcile.Result{}, nil - } - namespace := corev1.Namespace{} err := r.Client.Get(ctx, types.NamespacedName{Name: request.Namespace}, &namespace) if err != nil { diff --git a/controllers/gitopsservice_controller.go b/controllers/gitopsservice_controller.go index ace208c3c2e..46cc4c28c25 100644 --- a/controllers/gitopsservice_controller.go +++ b/controllers/gitopsservice_controller.go @@ -27,7 +27,6 @@ import ( argoapp "github.com/argoproj-labs/argocd-operator/api/v1beta1" argocommon "github.com/argoproj-labs/argocd-operator/common" argocdcontroller "github.com/argoproj-labs/argocd-operator/controllers/argocd" - argoutil "github.com/argoproj-labs/argocd-operator/controllers/argocd" argocdutil "github.com/argoproj-labs/argocd-operator/controllers/argoutil" "github.com/go-logr/logr" version "github.com/hashicorp/go-version" @@ -222,12 +221,6 @@ func (r *ReconcileGitopsService) Reconcile(ctx context.Context, request reconcil reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) reqLogger.Info("Reconciling GitopsService") - // Fetch the GitopsService instance if on OpenShift cluster - if !argoutil.IsOpenShiftCluster() { - reqLogger.Info("Skip GitopsService reconcile: Not an OpenShift cluster") - return reconcile.Result{}, nil - } - instance := &pipelinesv1alpha1.GitopsService{} err := r.Client.Get(ctx, types.NamespacedName{Name: serviceName}, instance) if err != nil { diff --git a/controllers/gitopsservice_controller_test.go b/controllers/gitopsservice_controller_test.go index 6c98d31c34c..cbb1945cc30 100644 --- a/controllers/gitopsservice_controller_test.go +++ b/controllers/gitopsservice_controller_test.go @@ -19,6 +19,7 @@ package controllers import ( "context" "fmt" + "os" "testing" argoapp "github.com/argoproj-labs/argocd-operator/api/v1beta1" @@ -48,6 +49,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) +func TestMain(m *testing.M) { + util.SetOpenShiftClusterFound(true) + util.SetMonitoringAPIFound(true) + os.Exit(m.Run()) +} + func TestImageFromEnvVariable(t *testing.T) { ns := types.NamespacedName{Name: "test", Namespace: "test"} t.Run("Image present as env variable", func(t *testing.T) { diff --git a/controllers/util/test_util.go b/controllers/util/test_util.go new file mode 100644 index 00000000000..689428da869 --- /dev/null +++ b/controllers/util/test_util.go @@ -0,0 +1,36 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** +func SetOpenShiftClusterFound(found bool) { + openShiftClusterFound = found +} + +func SetMonitoringAPIFound(found bool) { + monitoringAPIFound = found +} + +// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** +func SetConsoleAPIFound(found bool) { + consoleAPIFound = found +} + +// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** +func SetRouteAPIFound(found bool) { + routeAPIFound = found +} diff --git a/controllers/util/util.go b/controllers/util/util.go index f06671ec860..225b54ff116 100644 --- a/controllers/util/util.go +++ b/controllers/util/util.go @@ -40,9 +40,10 @@ const ( ) var ( - consoleAPIFound = false - routeAPIFound = false - monitoringAPIFound = false + consoleAPIFound = false + routeAPIFound = false + monitoringAPIFound = false + openShiftClusterFound = false ) // GetClusterVersion returns the OpenShift Cluster version in which the operator is installed @@ -76,25 +77,36 @@ func NewClusterVersion(version string) *configv1.ClusterVersion { } func InspectCluster() error { - if err := verifyConsoleAPI(); err != nil { + if err := verifyOpenShiftCluster(); err != nil { return err } if err := verifyRouteAPI(); err != nil { return err } + if err := verifyConsoleAPI(); err != nil { + return err + } if err := verifyMonitoringAPI(); err != nil { return err } return nil } -func IsConsoleAPIFound() bool { - return consoleAPIFound +func IsOpenShiftCluster() bool { + return openShiftClusterFound } -// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** -func SetConsoleAPIFound(found bool) { - consoleAPIFound = found +func verifyOpenShiftCluster() error { + found, err := argoutil.VerifyAPI(configv1.GroupName, configv1.GroupVersion.Version) + if err != nil { + return err + } + openShiftClusterFound = found + return nil +} + +func IsConsoleAPIFound() bool { + return consoleAPIFound } func verifyConsoleAPI() error { @@ -110,11 +122,6 @@ func IsRouteAPIFound() bool { return routeAPIFound } -// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** -func SetRouteAPIFound(found bool) { - routeAPIFound = found -} - func verifyRouteAPI() error { found, err := argoutil.VerifyAPI(routev1.GroupName, routev1.GroupVersion.Version) if err != nil { diff --git a/controllers/util/util_test.go b/controllers/util/util_test.go index 7b0c3a5ac31..2b55605a194 100644 --- a/controllers/util/util_test.go +++ b/controllers/util/util_test.go @@ -69,6 +69,23 @@ func TestRouteAPIFound(t *testing.T) { }) } +func TestOpenShiftClusterFound(t *testing.T) { + t.Run("OpenShift cluster not found by default", func(t *testing.T) { + defer SetOpenShiftClusterFound(false) + SetOpenShiftClusterFound(false) + if IsOpenShiftCluster() { + t.Fatal("expected OpenShift cluster to not be found") + } + }) + t.Run("SetOpenShiftClusterFound sets the value", func(t *testing.T) { + defer SetOpenShiftClusterFound(false) + SetOpenShiftClusterFound(true) + if !IsOpenShiftCluster() { + t.Fatal("expected OpenShift cluster to be found after SetOpenShiftClusterFound(true)") + } + }) +} + func TestConsoleAPIFound(t *testing.T) { t.Run("Console API not found by default", func(t *testing.T) { defer SetConsoleAPIFound(false) From 6d7e429f98efd75df1225ea2dedbf496a677ade0 Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Thu, 28 May 2026 13:10:30 +0530 Subject: [PATCH 4/8] remove unit tests Signed-off-by: Anand Kumar Singh --- controllers/util/util_test.go | 51 ----------------------------------- 1 file changed, 51 deletions(-) diff --git a/controllers/util/util_test.go b/controllers/util/util_test.go index 2b55605a194..2a254894519 100644 --- a/controllers/util/util_test.go +++ b/controllers/util/util_test.go @@ -52,57 +52,6 @@ func addKnownTypesToScheme(scheme *runtime.Scheme) { scheme.AddKnownTypes(configv1.GroupVersion, &configv1.ClusterVersion{}) } -func TestRouteAPIFound(t *testing.T) { - t.Run("Route API not found by default", func(t *testing.T) { - defer SetRouteAPIFound(false) - SetRouteAPIFound(false) - if IsRouteAPIFound() { - t.Fatal("expected Route API to not be found") - } - }) - t.Run("SetRouteAPIFound sets the value", func(t *testing.T) { - defer SetRouteAPIFound(false) - SetRouteAPIFound(true) - if !IsRouteAPIFound() { - t.Fatal("expected Route API to be found after SetRouteAPIFound(true)") - } - }) -} - -func TestOpenShiftClusterFound(t *testing.T) { - t.Run("OpenShift cluster not found by default", func(t *testing.T) { - defer SetOpenShiftClusterFound(false) - SetOpenShiftClusterFound(false) - if IsOpenShiftCluster() { - t.Fatal("expected OpenShift cluster to not be found") - } - }) - t.Run("SetOpenShiftClusterFound sets the value", func(t *testing.T) { - defer SetOpenShiftClusterFound(false) - SetOpenShiftClusterFound(true) - if !IsOpenShiftCluster() { - t.Fatal("expected OpenShift cluster to be found after SetOpenShiftClusterFound(true)") - } - }) -} - -func TestConsoleAPIFound(t *testing.T) { - t.Run("Console API not found by default", func(t *testing.T) { - defer SetConsoleAPIFound(false) - SetConsoleAPIFound(false) - if IsConsoleAPIFound() { - t.Fatal("expected Console API to not be found") - } - }) - t.Run("SetConsoleAPIFound sets the value", func(t *testing.T) { - defer SetConsoleAPIFound(false) - SetConsoleAPIFound(true) - if !IsConsoleAPIFound() { - t.Fatal("expected Console API to be found after SetConsoleAPIFound(true)") - } - }) -} - func assertNoError(t *testing.T, err error) { t.Helper() if err != nil { From 6fbf537c30abe7a7597cddab703540ab418a9121 Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Thu, 28 May 2026 16:16:07 +0530 Subject: [PATCH 5/8] run all api check before returning, add olm api check before register, keep the check and register pattern same for all conditional api registration assisted-by: claude-code for code,planning Signed-off-by: Anand Kumar Singh --- cmd/main.go | 29 +++---- controllers/argocd_controller.go | 5 +- controllers/gitopsservice_controller.go | 1 + controllers/gitopsservice_controller_test.go | 1 + controllers/util/test_util.go | 20 +++++ controllers/util/util.go | 86 ++++++++++++++++++-- 6 files changed, 115 insertions(+), 27 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 0aa0fd7cf40..b1a13ced1b7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -213,8 +213,12 @@ func main() { registerComponentOrExit(mgr, monitoringv1.AddToScheme) } - registerComponentOrExit(mgr, operatorsv1.AddToScheme) - registerComponentOrExit(mgr, operatorsv1alpha1.AddToScheme) + // Setup Scheme for OLM if available (verified by InspectCluster) + if util.IsOLMAPIFound() { + registerComponentOrExit(mgr, operatorsv1.AddToScheme) + registerComponentOrExit(mgr, operatorsv1alpha1.AddToScheme) + } + registerComponentOrExit(mgr, argov1alpha1api.AddToScheme) registerComponentOrExit(mgr, argov1beta1api.AddToScheme) @@ -225,27 +229,18 @@ func main() { registerComponentOrExit(mgr, rolloutManagerApi.AddToScheme) - // Setup Scheme for OpenShift Template if available - if found, err := argoutil.VerifyAPI(templatev1.GroupName, templatev1.GroupVersion.Version); err != nil { - setupLog.Error(err, "unable to verify Template API") - os.Exit(1) - } else if found { + // Setup Scheme for OpenShift Template if available (verified by InspectCluster) + if util.IsTemplateAPIFound() { registerComponentOrExit(mgr, templatev1.AddToScheme) } - // Setup Scheme for OpenShift Apps if available - if found, err := argoutil.VerifyAPI(appsv1.GroupName, appsv1.GroupVersion.Version); err != nil { - setupLog.Error(err, "unable to verify Apps API") - os.Exit(1) - } else if found { + // Setup Scheme for OpenShift Apps if available (verified by InspectCluster) + if util.IsAppsAPIFound() { registerComponentOrExit(mgr, appsv1.AddToScheme) } - // Setup Scheme for OpenShift OAuth if available - if found, err := argoutil.VerifyAPI(oauthv1.GroupName, oauthv1.GroupVersion.Version); err != nil { - setupLog.Error(err, "unable to verify OAuth API") - os.Exit(1) - } else if found { + // Setup Scheme for OpenShift OAuth if available (verified by InspectCluster) + if util.IsOAuthAPIFound() { registerComponentOrExit(mgr, oauthv1.AddToScheme) } diff --git a/controllers/argocd_controller.go b/controllers/argocd_controller.go index f083f9ce48d..0f82aae61e7 100644 --- a/controllers/argocd_controller.go +++ b/controllers/argocd_controller.go @@ -71,8 +71,9 @@ func isConsoleLinkDisabled() bool { // SetupWithManager sets up the controller with the Manager. func (r *ReconcileArgoCDRoute) SetupWithManager(mgr ctrl.Manager) error { - // Watch for changes to argocd-server route in the default argocd instance namespace - // The ConsoleLink holds the route URL and should be regenerated when route is updated + if !util.IsRouteAPIFound() { + return nil + } return ctrl.NewControllerManagedBy(mgr). For(&routev1.Route{}, builder.WithPredicates(filterPredicate(filterArgoCDRoute))). diff --git a/controllers/gitopsservice_controller.go b/controllers/gitopsservice_controller.go index 46cc4c28c25..1b0797f1056 100644 --- a/controllers/gitopsservice_controller.go +++ b/controllers/gitopsservice_controller.go @@ -221,6 +221,7 @@ func (r *ReconcileGitopsService) Reconcile(ctx context.Context, request reconcil reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) reqLogger.Info("Reconciling GitopsService") + // Fetch the GitopsService instance instance := &pipelinesv1alpha1.GitopsService{} err := r.Client.Get(ctx, types.NamespacedName{Name: serviceName}, instance) if err != nil { diff --git a/controllers/gitopsservice_controller_test.go b/controllers/gitopsservice_controller_test.go index cbb1945cc30..689efa712a4 100644 --- a/controllers/gitopsservice_controller_test.go +++ b/controllers/gitopsservice_controller_test.go @@ -52,6 +52,7 @@ import ( func TestMain(m *testing.M) { util.SetOpenShiftClusterFound(true) util.SetMonitoringAPIFound(true) + util.SetRouteAPIFound(true) os.Exit(m.Run()) } diff --git a/controllers/util/test_util.go b/controllers/util/test_util.go index 689428da869..e5a5c9a82fb 100644 --- a/controllers/util/test_util.go +++ b/controllers/util/test_util.go @@ -34,3 +34,23 @@ func SetConsoleAPIFound(found bool) { func SetRouteAPIFound(found bool) { routeAPIFound = found } + +// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** +func SetTemplateAPIFound(found bool) { + templateAPIFound = found +} + +// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** +func SetAppsAPIFound(found bool) { + appsAPIFound = found +} + +// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** +func SetOAuthAPIFound(found bool) { + oauthAPIFound = found +} + +// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** +func SetOLMAPIFound(found bool) { + olmAPIFound = found +} diff --git a/controllers/util/util.go b/controllers/util/util.go index 225b54ff116..fb5fbb66156 100644 --- a/controllers/util/util.go +++ b/controllers/util/util.go @@ -18,14 +18,19 @@ package util import ( "context" + stderrors "errors" "fmt" "os" "strings" "github.com/argoproj-labs/argocd-operator/controllers/argoutil" + oappsv1 "github.com/openshift/api/apps/v1" configv1 "github.com/openshift/api/config/v1" console "github.com/openshift/api/console/v1" + oauthv1 "github.com/openshift/api/oauth/v1" routev1 "github.com/openshift/api/route/v1" + templatev1 "github.com/openshift/api/template/v1" + operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "golang.org/x/mod/semver" corev1 "k8s.io/api/core/v1" @@ -44,6 +49,10 @@ var ( routeAPIFound = false monitoringAPIFound = false openShiftClusterFound = false + templateAPIFound = false + appsAPIFound = false + oauthAPIFound = false + olmAPIFound = false ) // GetClusterVersion returns the OpenShift Cluster version in which the operator is installed @@ -80,16 +89,25 @@ func InspectCluster() error { if err := verifyOpenShiftCluster(); err != nil { return err } - if err := verifyRouteAPI(); err != nil { - return err - } - if err := verifyConsoleAPI(); err != nil { - return err + if !openShiftClusterFound { + return nil } - if err := verifyMonitoringAPI(); err != nil { - return err + + var errs []error + for _, check := range []func() error{ + verifyRouteAPI, + verifyConsoleAPI, + verifyMonitoringAPI, + verifyTemplateAPI, + verifyAppsAPI, + verifyOAuthAPI, + verifyOLMAPI, + } { + if err := check(); err != nil { + errs = append(errs, err) + } } - return nil + return stderrors.Join(errs...) } func IsOpenShiftCluster() bool { @@ -147,6 +165,58 @@ func IsMonitoringAPIFound() bool { return monitoringAPIFound } +func IsTemplateAPIFound() bool { + return templateAPIFound +} + +func verifyTemplateAPI() error { + found, err := argoutil.VerifyAPI(templatev1.GroupName, templatev1.GroupVersion.Version) + if err != nil { + return err + } + templateAPIFound = found + return nil +} + +func IsAppsAPIFound() bool { + return appsAPIFound +} + +func verifyAppsAPI() error { + found, err := argoutil.VerifyAPI(oappsv1.GroupName, oappsv1.GroupVersion.Version) + if err != nil { + return err + } + appsAPIFound = found + return nil +} + +func IsOAuthAPIFound() bool { + return oauthAPIFound +} + +func verifyOAuthAPI() error { + found, err := argoutil.VerifyAPI(oauthv1.GroupName, oauthv1.GroupVersion.Version) + if err != nil { + return err + } + oauthAPIFound = found + return nil +} + +func IsOLMAPIFound() bool { + return olmAPIFound +} + +func verifyOLMAPI() error { + found, err := argoutil.VerifyAPI(operatorsv1.GroupVersion.Group, operatorsv1.GroupVersion.Version) + if err != nil { + return err + } + olmAPIFound = found + return nil +} + func ProxyEnvVars(vars ...corev1.EnvVar) []corev1.EnvVar { result := []corev1.EnvVar{} result = append(result, vars...) From a583353a406a0be48a75e37188bb41aae6ec0962 Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Fri, 29 May 2026 14:07:07 +0530 Subject: [PATCH 6/8] incorporate review comments remove non openshift check before config.openshift.io api check to make sure CRDs are not skipped, and not gate gitopsservice Signed-off-by: Anand Kumar Singh --- cmd/main.go | 20 ++++++++----------- controllers/util/util.go | 42 ++++++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index b1a13ced1b7..acbe0809fad 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -223,7 +223,7 @@ func main() { registerComponentOrExit(mgr, argov1beta1api.AddToScheme) // Setup Scheme for OpenShift Config if available - if util.IsOpenShiftCluster() { + if util.IsConfigAPIFound() { registerComponentOrExit(mgr, configv1.AddToScheme) } @@ -254,17 +254,13 @@ func main() { } } - if util.IsOpenShiftCluster() { - if err = (&controllers.ReconcileGitopsService{ - Client: client, - Scheme: mgr.GetScheme(), - DisableDefaultInstall: strings.ToLower(os.Getenv(common.DisableDefaultInstallEnvVar)) == "true", - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "GitopsService") - os.Exit(1) - } - } else { - setupLog.Info("OpenShift Cluster not found, skipping ReconcileGitopsService controller setup") + if err = (&controllers.ReconcileGitopsService{ + Client: client, + Scheme: mgr.GetScheme(), + DisableDefaultInstall: strings.ToLower(os.Getenv(common.DisableDefaultInstallEnvVar)) == "true", + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "GitopsService") + os.Exit(1) } if util.IsRouteAPIFound() { diff --git a/controllers/util/util.go b/controllers/util/util.go index fb5fbb66156..cafd9be51b6 100644 --- a/controllers/util/util.go +++ b/controllers/util/util.go @@ -45,14 +45,14 @@ const ( ) var ( - consoleAPIFound = false - routeAPIFound = false - monitoringAPIFound = false - openShiftClusterFound = false - templateAPIFound = false - appsAPIFound = false - oauthAPIFound = false - olmAPIFound = false + consoleAPIFound = false + routeAPIFound = false + monitoringAPIFound = false + configAPIFound = false + templateAPIFound = false + appsAPIFound = false + oauthAPIFound = false + olmAPIFound = false ) // GetClusterVersion returns the OpenShift Cluster version in which the operator is installed @@ -86,22 +86,28 @@ func NewClusterVersion(version string) *configv1.ClusterVersion { } func InspectCluster() error { - if err := verifyOpenShiftCluster(); err != nil { + var errs []error + if err := verifyOLMAPI(); err != nil { + errs = append(errs, err) + } + if err := verifyMonitoringAPI(); err != nil { + errs = append(errs, err) + } + + if err := verifyConfigAPI(); err != nil { + errs = append(errs, err) return err } - if !openShiftClusterFound { + if !configAPIFound { return nil } - var errs []error for _, check := range []func() error{ verifyRouteAPI, verifyConsoleAPI, - verifyMonitoringAPI, verifyTemplateAPI, verifyAppsAPI, verifyOAuthAPI, - verifyOLMAPI, } { if err := check(); err != nil { errs = append(errs, err) @@ -110,16 +116,18 @@ func InspectCluster() error { return stderrors.Join(errs...) } -func IsOpenShiftCluster() bool { - return openShiftClusterFound +// used as a shortcut to check if the cluster is an OpenShift cluster +func IsConfigAPIFound() bool { + return configAPIFound } -func verifyOpenShiftCluster() error { +// verify if the Config.Openshift.io API is found +func verifyConfigAPI() error { found, err := argoutil.VerifyAPI(configv1.GroupName, configv1.GroupVersion.Version) if err != nil { return err } - openShiftClusterFound = found + configAPIFound = found return nil } From 21318ad7dcee3f1587180b3ea6860de3254e6051 Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Fri, 29 May 2026 14:07:47 +0530 Subject: [PATCH 7/8] update tests Signed-off-by: Anand Kumar Singh --- controllers/gitopsservice_controller_test.go | 3 ++- controllers/util/test_util.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/controllers/gitopsservice_controller_test.go b/controllers/gitopsservice_controller_test.go index 689efa712a4..d0447e89962 100644 --- a/controllers/gitopsservice_controller_test.go +++ b/controllers/gitopsservice_controller_test.go @@ -50,9 +50,10 @@ import ( ) func TestMain(m *testing.M) { - util.SetOpenShiftClusterFound(true) + util.SetConfigAPIFound(true) util.SetMonitoringAPIFound(true) util.SetRouteAPIFound(true) + util.SetOLMAPIFound(true) os.Exit(m.Run()) } diff --git a/controllers/util/test_util.go b/controllers/util/test_util.go index e5a5c9a82fb..962a5ddbed2 100644 --- a/controllers/util/test_util.go +++ b/controllers/util/test_util.go @@ -17,8 +17,8 @@ limitations under the License. package util // *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** -func SetOpenShiftClusterFound(found bool) { - openShiftClusterFound = found +func SetConfigAPIFound(found bool) { + configAPIFound = found } func SetMonitoringAPIFound(found bool) { From 405a81ef6088492aecc61372d453de06ead2870b Mon Sep 17 00:00:00 2001 From: Anand Kumar Singh Date: Fri, 29 May 2026 14:35:49 +0530 Subject: [PATCH 8/8] fix failure to return accumulated error Signed-off-by: Anand Kumar Singh --- controllers/util/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/util/util.go b/controllers/util/util.go index cafd9be51b6..282ad211baf 100644 --- a/controllers/util/util.go +++ b/controllers/util/util.go @@ -96,7 +96,7 @@ func InspectCluster() error { if err := verifyConfigAPI(); err != nil { errs = append(errs, err) - return err + return stderrors.Join(errs...) } if !configAPIFound { return nil