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 = (¬ificationsConfig.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 }