github.com/argoproj-labs/argocd-operator@v0.10.0/main.go (about)

     1  /*
     2  Copyright 2021.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"crypto/tls"
    21  	"flag"
    22  	"fmt"
    23  	"os"
    24  	goruntime "runtime"
    25  	"strings"
    26  
    27  	"github.com/argoproj/argo-cd/v2/util/env"
    28  	monitoringv1 "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
    29  	appsv1 "github.com/openshift/api/apps/v1"
    30  	configv1 "github.com/openshift/api/config/v1"
    31  	oauthv1 "github.com/openshift/api/oauth/v1"
    32  	routev1 "github.com/openshift/api/route/v1"
    33  	templatev1 "github.com/openshift/api/template/v1"
    34  	"github.com/operator-framework/operator-sdk/pkg/k8sutil"
    35  	sdkVersion "github.com/operator-framework/operator-sdk/version"
    36  	"sigs.k8s.io/controller-runtime/pkg/cache"
    37  	"sigs.k8s.io/controller-runtime/pkg/manager"
    38  	"sigs.k8s.io/controller-runtime/pkg/webhook"
    39  
    40  	"github.com/argoproj-labs/argocd-operator/common"
    41  	"github.com/argoproj-labs/argocd-operator/controllers/argocd"
    42  	"github.com/argoproj-labs/argocd-operator/controllers/argocdexport"
    43  
    44  	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
    45  
    46  	notificationsConfig "github.com/argoproj-labs/argocd-operator/controllers/notificationsconfiguration"
    47  
    48  	// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
    49  	// to ensure that exec-entrypoint and run can make use of them.
    50  	_ "k8s.io/client-go/plugin/pkg/client/auth"
    51  
    52  	"go.uber.org/zap/zapcore"
    53  	"k8s.io/apimachinery/pkg/labels"
    54  	"k8s.io/apimachinery/pkg/runtime"
    55  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    56  	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
    57  	ctrl "sigs.k8s.io/controller-runtime"
    58  	"sigs.k8s.io/controller-runtime/pkg/healthz"
    59  	"sigs.k8s.io/controller-runtime/pkg/log/zap"
    60  
    61  	v1alpha1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1"
    62  	v1beta1 "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    63  	"github.com/argoproj-labs/argocd-operator/version"
    64  	//+kubebuilder:scaffold:imports
    65  )
    66  
    67  var (
    68  	scheme   = runtime.NewScheme()
    69  	setupLog = ctrl.Log.WithName("setup")
    70  )
    71  
    72  func init() {
    73  	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
    74  
    75  	utilruntime.Must(v1alpha1.AddToScheme(scheme))
    76  	utilruntime.Must(v1beta1.AddToScheme(scheme))
    77  	//+kubebuilder:scaffold:scheme
    78  }
    79  
    80  func printVersion() {
    81  	setupLog.Info(fmt.Sprintf("Go Version: %s", goruntime.Version()))
    82  	setupLog.Info(fmt.Sprintf("Go OS/Arch: %s/%s", goruntime.GOOS, goruntime.GOARCH))
    83  	setupLog.Info(fmt.Sprintf("Version of operator-sdk: %v", sdkVersion.Version))
    84  	setupLog.Info(fmt.Sprintf("Version of %s-operator: %v", common.ArgoCDAppName, version.Version))
    85  }
    86  
    87  func main() {
    88  	var metricsAddr string
    89  	var enableLeaderElection bool
    90  	var probeAddr string
    91  	var labelSelectorFlag string
    92  
    93  	var secureMetrics = false
    94  	var enableHTTP2 = false
    95  
    96  	flag.StringVar(&metricsAddr, "metrics-bind-address", fmt.Sprintf(":%d", common.OperatorMetricsPort), "The address the metric endpoint binds to.")
    97  	flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
    98  	flag.StringVar(&labelSelectorFlag, "label-selector", env.StringFromEnv(common.ArgoCDLabelSelectorKey, common.ArgoCDDefaultLabelSelector), "The label selector is used to map to a subset of ArgoCD instances to reconcile")
    99  	flag.BoolVar(&enableLeaderElection, "leader-elect", false,
   100  		"Enable leader election for controller manager. "+
   101  			"Enabling this will ensure there is only one active controller manager.")
   102  	flag.BoolVar(&enableHTTP2, "enable-http2", enableHTTP2, "If HTTP/2 should be enabled for the metrics and webhook servers.")
   103  	flag.BoolVar(&secureMetrics, "metrics-secure", secureMetrics, "If the metrics endpoint should be served securely.")
   104  
   105  	//Configure log level
   106  	logLevelStr := strings.ToLower(os.Getenv("LOG_LEVEL"))
   107  	logLevel := zapcore.InfoLevel
   108  	switch logLevelStr {
   109  	case "debug":
   110  		logLevel = zapcore.DebugLevel
   111  	case "info":
   112  		logLevel = zapcore.InfoLevel
   113  	case "warn":
   114  		logLevel = zapcore.WarnLevel
   115  	case "error":
   116  		logLevel = zapcore.ErrorLevel
   117  	case "panic":
   118  		logLevel = zapcore.PanicLevel
   119  	case "fatal":
   120  		logLevel = zapcore.FatalLevel
   121  	}
   122  
   123  	opts := zap.Options{
   124  		Level:       logLevel,
   125  		Development: true,
   126  	}
   127  	opts.BindFlags(flag.CommandLine)
   128  	flag.Parse()
   129  
   130  	disableHTTP2 := func(c *tls.Config) {
   131  		if enableHTTP2 {
   132  			return
   133  		}
   134  		c.NextProtos = []string{"http/1.1"}
   135  	}
   136  	webhookServerOptions := webhook.Options{
   137  		TLSOpts: []func(config *tls.Config){disableHTTP2},
   138  		Port:    9443,
   139  	}
   140  	webhookServer := webhook.NewServer(webhookServerOptions)
   141  
   142  	metricsServerOptions := metricsserver.Options{
   143  		SecureServing: secureMetrics,
   144  		BindAddress:   metricsAddr,
   145  		TLSOpts:       []func(*tls.Config){disableHTTP2},
   146  	}
   147  
   148  	ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
   149  
   150  	printVersion()
   151  
   152  	// Check the label selector format eg. "foo=bar"
   153  	if _, err := labels.Parse(labelSelectorFlag); err != nil {
   154  		setupLog.Error(err, "error parsing the labelSelector '%s'.", labelSelectorFlag)
   155  		os.Exit(1)
   156  	}
   157  	setupLog.Info(fmt.Sprintf("Watching labelselector \"%s\"", labelSelectorFlag))
   158  
   159  	// Inspect cluster to verify availability of extra features
   160  	if err := argocd.InspectCluster(); err != nil {
   161  		setupLog.Info("unable to inspect cluster")
   162  	}
   163  
   164  	namespace, err := k8sutil.GetWatchNamespace()
   165  	if err != nil {
   166  		setupLog.Error(err, "Failed to get watch namespace, defaulting to all namespace mode")
   167  	}
   168  	setupLog.Info(fmt.Sprintf("Watching namespace \"%s\"", namespace))
   169  
   170  	// Set default manager options
   171  	options := manager.Options{
   172  		Metrics:                metricsServerOptions,
   173  		WebhookServer:          webhookServer,
   174  		Scheme:                 scheme,
   175  		HealthProbeBindAddress: probeAddr,
   176  		LeaderElection:         enableLeaderElection,
   177  		LeaderElectionID:       "b674928d.argoproj.io",
   178  	}
   179  
   180  	if watchedNsCache := getDefaultWatchedNamespacesCacheOptions(); watchedNsCache != nil {
   181  		options.Cache = cache.Options{
   182  			DefaultNamespaces: watchedNsCache,
   183  		}
   184  	}
   185  
   186  	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options)
   187  	if err != nil {
   188  		setupLog.Error(err, "unable to start manager")
   189  		os.Exit(1)
   190  	}
   191  
   192  	setupLog.Info("Registering Components.")
   193  
   194  	// Setup Scheme for all resources
   195  	if err := v1alpha1.AddToScheme(mgr.GetScheme()); err != nil {
   196  		setupLog.Error(err, "")
   197  		os.Exit(1)
   198  	}
   199  
   200  	if err := v1beta1.AddToScheme(mgr.GetScheme()); err != nil {
   201  		setupLog.Error(err, "")
   202  		os.Exit(1)
   203  	}
   204  
   205  	// Setup Scheme for Prometheus if available.
   206  	if argocd.IsPrometheusAPIAvailable() {
   207  		if err := monitoringv1.AddToScheme(mgr.GetScheme()); err != nil {
   208  			setupLog.Error(err, "")
   209  			os.Exit(1)
   210  		}
   211  	}
   212  
   213  	// Setup Scheme for OpenShift Routes if available.
   214  	if argocd.IsRouteAPIAvailable() {
   215  		if err := routev1.Install(mgr.GetScheme()); err != nil {
   216  			setupLog.Error(err, "")
   217  			os.Exit(1)
   218  		}
   219  	}
   220  
   221  	// Set up the scheme for openshift config if available
   222  	if argocd.IsVersionAPIAvailable() {
   223  		if err := configv1.Install(mgr.GetScheme()); err != nil {
   224  			setupLog.Error(err, "")
   225  			os.Exit(1)
   226  		}
   227  	}
   228  
   229  	// Setup Schemes for SSO if template instance is available.
   230  	if argocd.IsTemplateAPIAvailable() {
   231  		if err := templatev1.Install(mgr.GetScheme()); err != nil {
   232  			setupLog.Error(err, "")
   233  			os.Exit(1)
   234  		}
   235  		if err := appsv1.Install(mgr.GetScheme()); err != nil {
   236  			setupLog.Error(err, "")
   237  			os.Exit(1)
   238  		}
   239  		if err := oauthv1.Install(mgr.GetScheme()); err != nil {
   240  			setupLog.Error(err, "")
   241  			os.Exit(1)
   242  		}
   243  	}
   244  
   245  	if err = (&argocd.ReconcileArgoCD{
   246  		Client:        mgr.GetClient(),
   247  		Scheme:        mgr.GetScheme(),
   248  		LabelSelector: labelSelectorFlag,
   249  	}).SetupWithManager(mgr); err != nil {
   250  		setupLog.Error(err, "unable to create controller", "controller", "ArgoCD")
   251  		os.Exit(1)
   252  	}
   253  	if err = (&argocdexport.ReconcileArgoCDExport{
   254  		Client: mgr.GetClient(),
   255  		Scheme: mgr.GetScheme(),
   256  	}).SetupWithManager(mgr); err != nil {
   257  		setupLog.Error(err, "unable to create controller", "controller", "ArgoCDExport")
   258  		os.Exit(1)
   259  	}
   260  	if err = (&notificationsConfig.NotificationsConfigurationReconciler{
   261  		Client: mgr.GetClient(),
   262  		Scheme: mgr.GetScheme(),
   263  	}).SetupWithManager(mgr); err != nil {
   264  		setupLog.Error(err, "unable to create controller", "controller", "NotificationsConfiguration")
   265  		os.Exit(1)
   266  	}
   267  
   268  	// Start webhook only if ENABLE_CONVERSION_WEBHOOK is set
   269  	if strings.EqualFold(os.Getenv("ENABLE_CONVERSION_WEBHOOK"), "true") {
   270  		if err = (&v1beta1.ArgoCD{}).SetupWebhookWithManager(mgr); err != nil {
   271  			setupLog.Error(err, "unable to create webhook", "webhook", "ArgoCD")
   272  			os.Exit(1)
   273  		}
   274  	}
   275  	//+kubebuilder:scaffold:builder
   276  
   277  	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
   278  		setupLog.Error(err, "unable to set up health check")
   279  		os.Exit(1)
   280  	}
   281  	if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
   282  		setupLog.Error(err, "unable to set up ready check")
   283  		os.Exit(1)
   284  	}
   285  
   286  	setupLog.Info("starting manager")
   287  	if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
   288  		setupLog.Error(err, "problem running manager")
   289  		os.Exit(1)
   290  	}
   291  }
   292  
   293  func getDefaultWatchedNamespacesCacheOptions() map[string]cache.Config {
   294  	watchedNamespaces, err := k8sutil.GetWatchNamespace()
   295  	if err != nil {
   296  		setupLog.Error(err, "Failed to get watch namespace, defaulting to all namespace mode")
   297  		return nil
   298  	}
   299  
   300  	if watchedNamespaces == "" {
   301  		return nil
   302  	}
   303  
   304  	watchedNsList := strings.Split(watchedNamespaces, ",")
   305  	setupLog.Info(fmt.Sprintf("Watching namespaces: %v", watchedNsList))
   306  
   307  	defaultNamespacesCacheConfig := map[string]cache.Config{}
   308  	for _, ns := range watchedNsList {
   309  		defaultNamespacesCacheConfig[ns] = cache.Config{}
   310  	}
   311  
   312  	return defaultNamespacesCacheConfig
   313  }