diff --git a/cmd/main.go b/cmd/main.go index 32bbb1b4427..acbe0809fad 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,52 @@ func main() { client = mgr.GetClient() } - registerComponentOrExit(mgr, console.AddToScheme) - registerComponentOrExit(mgr, routev1.AddToScheme) // Adding the routev1 api - registerComponentOrExit(mgr, operatorsv1.AddToScheme) - registerComponentOrExit(mgr, operatorsv1alpha1.AddToScheme) + // 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) + } + + // Setup Scheme for Prometheus Operator if available (verified by InspectCluster) + if util.IsMonitoringAPIFound() { + registerComponentOrExit(mgr, monitoringv1.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) - registerComponentOrExit(mgr, configv1.AddToScheme) - registerComponentOrExit(mgr, monitoringv1.AddToScheme) + + // Setup Scheme for OpenShift Config if available + if util.IsConfigAPIFound() { + registerComponentOrExit(mgr, configv1.AddToScheme) + } + registerComponentOrExit(mgr, rolloutManagerApi.AddToScheme) - registerComponentOrExit(mgr, templatev1.AddToScheme) - registerComponentOrExit(mgr, appsv1.AddToScheme) - registerComponentOrExit(mgr, oauthv1.AddToScheme) + + // Setup Scheme for OpenShift Template if available (verified by InspectCluster) + if util.IsTemplateAPIFound() { + registerComponentOrExit(mgr, templatev1.AddToScheme) + } + + // Setup Scheme for OpenShift Apps if available (verified by InspectCluster) + if util.IsAppsAPIFound() { + registerComponentOrExit(mgr, appsv1.AddToScheme) + } + + // Setup Scheme for OpenShift OAuth if available (verified by InspectCluster) + if util.IsOAuthAPIFound() { + registerComponentOrExit(mgr, oauthv1.AddToScheme) + } + registerComponentOrExit(mgr, crdv1.AddToScheme) // Start webhook only if ENABLE_CONVERSION_WEBHOOK is set @@ -229,21 +263,30 @@ 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{ - 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_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 bf8d602bb9c..1b0797f1056 100644 --- a/controllers/gitopsservice_controller.go +++ b/controllers/gitopsservice_controller.go @@ -101,15 +101,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{}, @@ -344,14 +349,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..d0447e89962 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,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) +func TestMain(m *testing.M) { + util.SetConfigAPIFound(true) + util.SetMonitoringAPIFound(true) + util.SetRouteAPIFound(true) + util.SetOLMAPIFound(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) { @@ -927,6 +936,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 +958,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/test_util.go b/controllers/util/test_util.go new file mode 100644 index 00000000000..962a5ddbed2 --- /dev/null +++ b/controllers/util/test_util.go @@ -0,0 +1,56 @@ +/* +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 SetConfigAPIFound(found bool) { + configAPIFound = 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 +} + +// *** 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 50fe4c23869..282ad211baf 100644 --- a/controllers/util/util.go +++ b/controllers/util/util.go @@ -18,13 +18,20 @@ 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" "k8s.io/apimachinery/pkg/api/errors" @@ -38,7 +45,14 @@ const ( ) var ( - consoleAPIFound = 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 @@ -72,9 +86,48 @@ func NewClusterVersion(version string) *configv1.ClusterVersion { } func InspectCluster() error { - if err := verifyConsoleAPI(); 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 stderrors.Join(errs...) + } + if !configAPIFound { + return nil + } + + for _, check := range []func() error{ + verifyRouteAPI, + verifyConsoleAPI, + verifyTemplateAPI, + verifyAppsAPI, + verifyOAuthAPI, + } { + if err := check(); err != nil { + errs = append(errs, err) + } + } + return stderrors.Join(errs...) +} + +// used as a shortcut to check if the cluster is an OpenShift cluster +func IsConfigAPIFound() bool { + return configAPIFound +} + +// 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 } + configAPIFound = found return nil } @@ -82,11 +135,6 @@ func IsConsoleAPIFound() bool { return consoleAPIFound } -// *** THIS SHOULD ONLY BE USED FOR UNIT TESTING *** -func SetConsoleAPIFound(found bool) { - consoleAPIFound = found -} - func verifyConsoleAPI() error { found, err := argoutil.VerifyAPI(console.GroupName, console.GroupVersion.Version) if err != nil { @@ -96,6 +144,87 @@ func verifyConsoleAPI() error { return nil } +func IsRouteAPIFound() bool { + return routeAPIFound +} + +func verifyRouteAPI() error { + found, err := argoutil.VerifyAPI(routev1.GroupName, routev1.GroupVersion.Version) + if err != nil { + return err + } + routeAPIFound = found + 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 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...)