diff --git a/Dockerfile b/Dockerfile index e1e19a9fa..5d7026542 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,7 @@ COPY api/ api/ COPY common/ common/ COPY controllers/ controllers/ COPY version/ version/ +COPY pkg/ pkg/ # Build ARG LD_FLAGS diff --git a/cmd/main.go b/cmd/main.go index b2efb25c0..ffa8e8ebf 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -25,12 +25,6 @@ import ( "strings" "github.com/argoproj/argo-cd/v3/util/env" - appsv1 "github.com/openshift/api/apps/v1" - configv1 "github.com/openshift/api/config/v1" - oauthv1 "github.com/openshift/api/oauth/v1" - routev1 "github.com/openshift/api/route/v1" - templatev1 "github.com/openshift/api/template/v1" - monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client/config" @@ -40,6 +34,7 @@ import ( "github.com/argoproj-labs/argocd-operator/common" "github.com/argoproj-labs/argocd-operator/controllers/argocd" "github.com/argoproj-labs/argocd-operator/controllers/argocdexport" + operatorscheme "github.com/argoproj-labs/argocd-operator/pkg/schema" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" @@ -52,13 +47,10 @@ import ( "go.uber.org/zap/zapcore" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" - v1alpha1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1" v1beta1 "github.com/argoproj-labs/argocd-operator/api/v1beta1" "github.com/argoproj-labs/argocd-operator/version" //+kubebuilder:scaffold:imports @@ -69,14 +61,6 @@ var ( setupLog = ctrl.Log.WithName("setup") ) -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - - utilruntime.Must(v1alpha1.AddToScheme(scheme)) - utilruntime.Must(v1beta1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme -} - func printVersion() { setupLog.Info(fmt.Sprintf("Go Version: %s", goruntime.Version())) setupLog.Info(fmt.Sprintf("Go OS/Arch: %s/%s", goruntime.GOOS, goruntime.GOARCH)) @@ -190,65 +174,25 @@ func main() { setupLog.Info("Registering Components.") - // Setup Scheme for all resources - if err := v1alpha1.AddToScheme(mgr.GetScheme()); err != nil { - setupLog.Error(err, "") - os.Exit(1) - } + // Setup Scheme for all resources using the centralized scheme package + err1 := operatorscheme.SetupScheme(mgr.GetScheme(), operatorscheme.SchemeOptions{ + EnablePrometheus: argocd.IsPrometheusAPIAvailable(), + EnableRoutes: argocd.IsRouteAPIAvailable(), + EnableVersion: argocd.IsVersionAPIAvailable(), + EnableKeycloak: argocd.CanUseKeycloakWithTemplate(), + }) - if err := v1beta1.AddToScheme(mgr.GetScheme()); err != nil { - setupLog.Error(err, "") + if err1 != nil { + setupLog.Error(err, "failed to set up scheme: %v") os.Exit(1) } - // Setup Scheme for Prometheus if available. - if argocd.IsPrometheusAPIAvailable() { - if err := monitoringv1.AddToScheme(mgr.GetScheme()); err != nil { - setupLog.Error(err, "") - os.Exit(1) - } - } - - // Setup Scheme for OpenShift Routes if available. - if argocd.IsRouteAPIAvailable() { - if err := routev1.Install(mgr.GetScheme()); err != nil { - setupLog.Error(err, "") - os.Exit(1) - } - } - - // Set up the scheme for openshift config if available - if argocd.IsVersionAPIAvailable() { - if err := configv1.Install(mgr.GetScheme()); err != nil { - setupLog.Error(err, "") - os.Exit(1) - } - } - - // Setup Schemes for SSO if template instance is available. - if argocd.CanUseKeycloakWithTemplate() { - setupLog.Info("Keycloak instance can be managed using OpenShift Template") - if err := templatev1.Install(mgr.GetScheme()); err != nil { - setupLog.Error(err, "") - os.Exit(1) - } - if err := appsv1.Install(mgr.GetScheme()); err != nil { - setupLog.Error(err, "") - os.Exit(1) - } - if err := oauthv1.Install(mgr.GetScheme()); err != nil { - setupLog.Error(err, "") - os.Exit(1) - } - } else { - setupLog.Info("Keycloak instance cannot be managed using OpenShift Template, as DeploymentConfig/Template API is not present") - } - k8sClient, err := initK8sClient() if err != nil { setupLog.Error(err, "Failed to initialize Kubernetes client") os.Exit(1) } + if err = (&argocd.ReconcileArgoCD{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), diff --git a/pkg/schema/scheme.go b/pkg/schema/scheme.go new file mode 100644 index 000000000..61e67c713 --- /dev/null +++ b/pkg/schema/scheme.go @@ -0,0 +1,98 @@ +/* +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 scheme provides centralized scheme registration for all API types +package schema + +import ( + appsv1 "github.com/openshift/api/apps/v1" + configv1 "github.com/openshift/api/config/v1" + oauthv1 "github.com/openshift/api/oauth/v1" + routev1 "github.com/openshift/api/route/v1" + templatev1 "github.com/openshift/api/template/v1" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + + v1alpha1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1" + v1beta1 "github.com/argoproj-labs/argocd-operator/api/v1beta1" +) + +var ( + schemeLog = ctrl.Log.WithName("scheme") +) + +// SchemeOptions provides toggles to register optional APIs. +type SchemeOptions struct { + EnablePrometheus bool + EnableRoutes bool + EnableVersion bool + EnableKeycloak bool +} + +func SetupScheme(scheme *runtime.Scheme, opts SchemeOptions) error { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + registerArgoCDAPIs(scheme) + // Setup Scheme for Prometheus if enabled. + if opts.EnablePrometheus { + if err := monitoringv1.AddToScheme(scheme); err != nil { + schemeLog.Error(err, "Failed to register Prometheus API") + return err + } + } + // Setup Scheme for OpenShift Routes if enabled. + if opts.EnableRoutes { + if err := routev1.Install(scheme); err != nil { + schemeLog.Error(err, "Failed to register Route API") + return err + } + } + // Setup Scheme for OpenShift config if enabled. + if opts.EnableVersion { + if err := configv1.Install(scheme); err != nil { + schemeLog.Error(err, "Failed to register Version API") + return err + } + } + // Setup Schemes for SSO if template instance is available. + if opts.EnableKeycloak { + schemeLog.Info("Keycloak instance can be managed using OpenShift Template.") + if err := templatev1.Install(scheme); err != nil { + schemeLog.Error(err, "Failed to register Template API") + return err + } + if err := appsv1.Install(scheme); err != nil { + schemeLog.Error(err, "Failed to register DeploymentConfig API") + return err + } + if err := oauthv1.Install(scheme); err != nil { + schemeLog.Error(err, "Failed to register OAuth API") + return err + } + } else { + schemeLog.Info("Keycloak instance cannot be managed using OpenShift Template, as //DeploymentConfig/Template API is not present.") + } + + schemeLog.Info("Scheme setup complete.") + return nil +} + +func registerArgoCDAPIs(scheme *runtime.Scheme) { + utilruntime.Must(v1alpha1.AddToScheme(scheme)) + utilruntime.Must(v1beta1.AddToScheme(scheme)) +} diff --git a/pkg/schema/scheme_test.go b/pkg/schema/scheme_test.go new file mode 100644 index 000000000..44e616e45 --- /dev/null +++ b/pkg/schema/scheme_test.go @@ -0,0 +1,73 @@ +package schema + +import ( + "testing" + + "github.com/argoproj-labs/argocd-operator/controllers/argocd" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func TestSetupScheme(t *testing.T) { + t.Run("SetupScheme should register all required schemes", func(t *testing.T) { + scheme := runtime.NewScheme() + + // Call SetupScheme + SetupScheme(scheme, SchemeOptions{ + EnablePrometheus: true, + EnableRoutes: true, + EnableVersion: true, + EnableKeycloak: true, + }) + + // Verify the scheme is not nil + assert.NotNil(t, scheme, "Scheme should not be nil") + + // Verify that ArgoCD API schemes are registered + v1alpha1GVK := schema.GroupVersionKind{Group: "argoproj.io", Version: "v1alpha1", Kind: "ArgoCD"} + v1beta1GVK := schema.GroupVersionKind{Group: "argoproj.io", Version: "v1beta1", Kind: "ArgoCD"} + assert.True(t, scheme.Recognizes(v1alpha1GVK), "v1alpha1 ArgoCD scheme should be registered") + assert.True(t, scheme.Recognizes(v1beta1GVK), "v1beta1 ArgoCD scheme should be registered") + + // Verify conditional schemes based on API availability + if argocd.IsPrometheusAPIAvailable() { + prometheusGVK := schema.GroupVersionKind{Group: "monitoring.coreos.com", Version: "v1", Kind: "Prometheus"} + assert.True(t, scheme.Recognizes(prometheusGVK), "Prometheus scheme should be registered when API is available") + } + + if argocd.IsRouteAPIAvailable() { + routeGVK := schema.GroupVersionKind{Group: "route.openshift.io", Version: "v1", Kind: "Route"} + assert.True(t, scheme.Recognizes(routeGVK), "OpenShift Route scheme should be registered when API is available") + } + + if argocd.IsVersionAPIAvailable() { + clusterVersionGVK := schema.GroupVersionKind{Group: "config.openshift.io", Version: "v1", Kind: "ClusterVersion"} + assert.True(t, scheme.Recognizes(clusterVersionGVK), "OpenShift Config scheme should be registered when API is available") + } + + if argocd.CanUseKeycloakWithTemplate() { + templateGVK := schema.GroupVersionKind{Group: "template.openshift.io", Version: "v1", Kind: "Template"} + deploymentConfigGVK := schema.GroupVersionKind{Group: "apps.openshift.io", Version: "v1", Kind: "DeploymentConfig"} + oauthClientGVK := schema.GroupVersionKind{Group: "oauth.openshift.io", Version: "v1", Kind: "OAuthClient"} + + assert.True(t, scheme.Recognizes(templateGVK), "OpenShift Template scheme should be registered when Keycloak can use templates") + assert.True(t, scheme.Recognizes(deploymentConfigGVK), "OpenShift DeploymentConfig scheme should be registered when Keycloak can use templates") + assert.True(t, scheme.Recognizes(oauthClientGVK), "OpenShift OAuth scheme should be registered when Keycloak can use templates") + } + }) + + t.Run("SetupScheme should not panic with empty scheme", func(t *testing.T) { + scheme := runtime.NewScheme() + + // This should not panic + assert.NotPanics(t, func() { + SetupScheme(scheme, SchemeOptions{ + EnablePrometheus: true, + EnableRoutes: true, + EnableVersion: true, + EnableKeycloak: true, + }) + }, "SetupScheme should not panic with empty scheme") + }) +}