github.com/argoproj/argo-cd/v3@v3.2.1/cmd/argocd-server/commands/argocd_server.go (about) 1 package commands 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "runtime/debug" 8 "strings" 9 "time" 10 11 "github.com/redis/go-redis/v9" 12 "k8s.io/apimachinery/pkg/runtime" 13 clientgoscheme "k8s.io/client-go/kubernetes/scheme" 14 15 "github.com/argoproj/pkg/v2/stats" 16 log "github.com/sirupsen/logrus" 17 "github.com/spf13/cobra" 18 "k8s.io/client-go/dynamic" 19 "k8s.io/client-go/kubernetes" 20 "k8s.io/client-go/tools/clientcmd" 21 "sigs.k8s.io/controller-runtime/pkg/client" 22 23 cmdutil "github.com/argoproj/argo-cd/v3/cmd/util" 24 "github.com/argoproj/argo-cd/v3/common" 25 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 26 appclientset "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned" 27 "github.com/argoproj/argo-cd/v3/reposerver/apiclient" 28 reposervercache "github.com/argoproj/argo-cd/v3/reposerver/cache" 29 "github.com/argoproj/argo-cd/v3/server" 30 servercache "github.com/argoproj/argo-cd/v3/server/cache" 31 "github.com/argoproj/argo-cd/v3/util/argo" 32 cacheutil "github.com/argoproj/argo-cd/v3/util/cache" 33 "github.com/argoproj/argo-cd/v3/util/cli" 34 "github.com/argoproj/argo-cd/v3/util/dex" 35 "github.com/argoproj/argo-cd/v3/util/env" 36 "github.com/argoproj/argo-cd/v3/util/errors" 37 "github.com/argoproj/argo-cd/v3/util/kube" 38 "github.com/argoproj/argo-cd/v3/util/templates" 39 "github.com/argoproj/argo-cd/v3/util/tls" 40 traceutil "github.com/argoproj/argo-cd/v3/util/trace" 41 ) 42 43 const ( 44 failureRetryCountEnv = "ARGOCD_K8S_RETRY_COUNT" 45 failureRetryPeriodMilliSecondsEnv = "ARGOCD_K8S_RETRY_DURATION_MILLISECONDS" 46 ) 47 48 var ( 49 failureRetryCount = env.ParseNumFromEnv(failureRetryCountEnv, 0, 0, 10) 50 failureRetryPeriodMilliSeconds = env.ParseNumFromEnv(failureRetryPeriodMilliSecondsEnv, 100, 0, 1000) 51 gitSubmoduleEnabled = env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true) 52 ) 53 54 // NewCommand returns a new instance of an argocd command 55 func NewCommand() *cobra.Command { 56 var ( 57 redisClient *redis.Client 58 insecure bool 59 listenHost string 60 listenPort int 61 metricsHost string 62 metricsPort int 63 otlpAddress string 64 otlpInsecure bool 65 otlpHeaders map[string]string 66 otlpAttrs []string 67 glogLevel int 68 clientConfig clientcmd.ClientConfig 69 repoServerTimeoutSeconds int 70 baseHRef string 71 rootPath string 72 repoServerAddress string 73 dexServerAddress string 74 disableAuth bool 75 contentTypes string 76 enableGZip bool 77 tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error) 78 cacheSrc func() (*servercache.Cache, error) 79 repoServerCacheSrc func() (*reposervercache.Cache, error) 80 frameOptions string 81 contentSecurityPolicy string 82 repoServerPlaintext bool 83 repoServerStrictTLS bool 84 dexServerPlaintext bool 85 dexServerStrictTLS bool 86 staticAssetsDir string 87 applicationNamespaces []string 88 enableProxyExtension bool 89 webhookParallelism int 90 hydratorEnabled bool 91 syncWithReplaceAllowed bool 92 93 // ApplicationSet 94 enableNewGitFileGlobbing bool 95 scmRootCAPath string 96 allowedScmProviders []string 97 enableScmProviders bool 98 enableGitHubAPIMetrics bool 99 100 // argocd k8s event logging flag 101 enableK8sEvent []string 102 ) 103 command := &cobra.Command{ 104 Use: cliName, 105 Short: "Run the ArgoCD API server", 106 Long: "The API server is a gRPC/REST server which exposes the API consumed by the Web UI, CLI, and CI/CD systems. This command runs API server in the foreground. It can be configured by following options.", 107 DisableAutoGenTag: true, 108 Run: func(c *cobra.Command, _ []string) { 109 ctx := c.Context() 110 111 vers := common.GetVersion() 112 namespace, _, err := clientConfig.Namespace() 113 errors.CheckError(err) 114 vers.LogStartupInfo( 115 "ArgoCD API Server", 116 map[string]any{ 117 "namespace": namespace, 118 "port": listenPort, 119 }, 120 ) 121 122 cli.SetLogFormat(cmdutil.LogFormat) 123 cli.SetLogLevel(cmdutil.LogLevel) 124 cli.SetGLogLevel(glogLevel) 125 126 // Recover from panic and log the error using the configured logger instead of the default. 127 defer func() { 128 if r := recover(); r != nil { 129 log.WithField("trace", string(debug.Stack())).Fatal("Recovered from panic: ", r) 130 } 131 }() 132 133 config, err := clientConfig.ClientConfig() 134 errors.CheckError(err) 135 errors.CheckError(v1alpha1.SetK8SConfigDefaults(config)) 136 137 tlsConfigCustomizer, err := tlsConfigCustomizerSrc() 138 errors.CheckError(err) 139 cache, err := cacheSrc() 140 errors.CheckError(err) 141 repoServerCache, err := repoServerCacheSrc() 142 errors.CheckError(err) 143 144 kubeclientset := kubernetes.NewForConfigOrDie(config) 145 146 appclientsetConfig, err := clientConfig.ClientConfig() 147 errors.CheckError(err) 148 errors.CheckError(v1alpha1.SetK8SConfigDefaults(appclientsetConfig)) 149 config.UserAgent = fmt.Sprintf("argocd-server/%s (%s)", vers.Version, vers.Platform) 150 151 if failureRetryCount > 0 { 152 appclientsetConfig = kube.AddFailureRetryWrapper(appclientsetConfig, failureRetryCount, failureRetryPeriodMilliSeconds) 153 } 154 appClientSet := appclientset.NewForConfigOrDie(appclientsetConfig) 155 tlsConfig := apiclient.TLSConfiguration{ 156 DisableTLS: repoServerPlaintext, 157 StrictValidation: repoServerStrictTLS, 158 } 159 160 dynamicClient := dynamic.NewForConfigOrDie(config) 161 162 scheme := runtime.NewScheme() 163 _ = clientgoscheme.AddToScheme(scheme) 164 _ = v1alpha1.AddToScheme(scheme) 165 166 controllerClient, err := client.New(config, client.Options{Scheme: scheme}) 167 errors.CheckError(err) 168 controllerClient = client.NewDryRunClient(controllerClient) 169 controllerClient = client.NewNamespacedClient(controllerClient, namespace) 170 171 // Load CA information to use for validating connections to the 172 // repository server, if strict TLS validation was requested. 173 if !repoServerPlaintext && repoServerStrictTLS { 174 pool, err := tls.LoadX509CertPool( 175 env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)+"/server/tls/tls.crt", 176 env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)+"/server/tls/ca.crt", 177 ) 178 if err != nil { 179 log.Fatalf("%v", err) 180 } 181 tlsConfig.Certificates = pool 182 } 183 184 dexTLSConfig := &dex.DexTLSConfig{ 185 DisableTLS: dexServerPlaintext, 186 StrictValidation: dexServerStrictTLS, 187 } 188 189 if !dexServerPlaintext && dexServerStrictTLS { 190 pool, err := tls.LoadX509CertPool( 191 env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath) + "/dex/tls/ca.crt", 192 ) 193 if err != nil { 194 log.Fatalf("%v", err) 195 } 196 dexTLSConfig.RootCAs = pool 197 cert, err := tls.LoadX509Cert( 198 env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath) + "/dex/tls/tls.crt", 199 ) 200 if err != nil { 201 log.Fatalf("%v", err) 202 } 203 dexTLSConfig.Certificate = cert.Raw 204 } 205 206 repoclientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds, tlsConfig) 207 if rootPath != "" { 208 if baseHRef != "" && baseHRef != rootPath { 209 log.Warnf("--basehref and --rootpath had conflict: basehref: %s rootpath: %s", baseHRef, rootPath) 210 } 211 baseHRef = rootPath 212 } 213 214 var contentTypesList []string 215 if contentTypes != "" { 216 contentTypesList = strings.Split(contentTypes, ";") 217 } 218 219 argoCDOpts := server.ArgoCDServerOpts{ 220 Insecure: insecure, 221 ListenPort: listenPort, 222 ListenHost: listenHost, 223 MetricsPort: metricsPort, 224 MetricsHost: metricsHost, 225 Namespace: namespace, 226 BaseHRef: baseHRef, 227 RootPath: rootPath, 228 DynamicClientset: dynamicClient, 229 KubeControllerClientset: controllerClient, 230 KubeClientset: kubeclientset, 231 AppClientset: appClientSet, 232 RepoClientset: repoclientset, 233 DexServerAddr: dexServerAddress, 234 DexTLSConfig: dexTLSConfig, 235 DisableAuth: disableAuth, 236 ContentTypes: contentTypesList, 237 EnableGZip: enableGZip, 238 TLSConfigCustomizer: tlsConfigCustomizer, 239 Cache: cache, 240 RepoServerCache: repoServerCache, 241 XFrameOptions: frameOptions, 242 ContentSecurityPolicy: contentSecurityPolicy, 243 RedisClient: redisClient, 244 StaticAssetsDir: staticAssetsDir, 245 ApplicationNamespaces: applicationNamespaces, 246 EnableProxyExtension: enableProxyExtension, 247 WebhookParallelism: webhookParallelism, 248 EnableK8sEvent: enableK8sEvent, 249 HydratorEnabled: hydratorEnabled, 250 SyncWithReplaceAllowed: syncWithReplaceAllowed, 251 } 252 253 appsetOpts := server.ApplicationSetOpts{ 254 GitSubmoduleEnabled: gitSubmoduleEnabled, 255 EnableNewGitFileGlobbing: enableNewGitFileGlobbing, 256 ScmRootCAPath: scmRootCAPath, 257 AllowedScmProviders: allowedScmProviders, 258 EnableScmProviders: enableScmProviders, 259 EnableGitHubAPIMetrics: enableGitHubAPIMetrics, 260 } 261 262 stats.RegisterStackDumper() 263 stats.StartStatsTicker(10 * time.Minute) 264 stats.RegisterHeapDumper("memprofile") 265 argocd := server.NewServer(ctx, argoCDOpts, appsetOpts) 266 argocd.Init(ctx) 267 for { 268 var closer func() 269 serverCtx, cancel := context.WithCancel(ctx) 270 lns, err := argocd.Listen() 271 errors.CheckError(err) 272 if otlpAddress != "" { 273 closer, err = traceutil.InitTracer(serverCtx, "argocd-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs) 274 if err != nil { 275 log.Fatalf("failed to initialize tracing: %v", err) 276 } 277 } 278 argocd.Run(serverCtx, lns) 279 if closer != nil { 280 closer() 281 } 282 cancel() 283 if argocd.TerminateRequested() { 284 break 285 } 286 } 287 }, 288 Example: templates.Examples(` 289 # Start the Argo CD API server with default settings 290 $ argocd-server 291 292 # Start the Argo CD API server on a custom port and enable tracing 293 $ argocd-server --port 8888 --otlp-address localhost:4317 294 `), 295 } 296 297 clientConfig = cli.AddKubectlFlagsToCmd(command) 298 command.Flags().BoolVar(&insecure, "insecure", env.ParseBoolFromEnv("ARGOCD_SERVER_INSECURE", false), "Run server without TLS") 299 command.Flags().StringVar(&staticAssetsDir, "staticassets", env.StringFromEnv("ARGOCD_SERVER_STATIC_ASSETS", "/shared/app"), "Directory path that contains additional static assets") 300 command.Flags().StringVar(&baseHRef, "basehref", env.StringFromEnv("ARGOCD_SERVER_BASEHREF", "/"), "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /") 301 command.Flags().StringVar(&rootPath, "rootpath", env.StringFromEnv("ARGOCD_SERVER_ROOTPATH", ""), "Used if Argo CD is running behind reverse proxy under subpath different from /") 302 command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_SERVER_LOGFORMAT", "json"), "Set the logging format. One of: json|text") 303 command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_SERVER_LOG_LEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") 304 command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level") 305 command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_SERVER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address") 306 command.Flags().StringVar(&dexServerAddress, "dex-server", env.StringFromEnv("ARGOCD_SERVER_DEX_SERVER", common.DefaultDexServerAddr), "Dex server address") 307 command.Flags().BoolVar(&disableAuth, "disable-auth", env.ParseBoolFromEnv("ARGOCD_SERVER_DISABLE_AUTH", false), "Disable client authentication") 308 command.Flags().StringVar(&contentTypes, "api-content-types", env.StringFromEnv("ARGOCD_API_CONTENT_TYPES", "application/json", env.StringFromEnvOpts{AllowEmpty: true}), "Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty.") 309 command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", true), "Enable GZIP compression") 310 command.AddCommand(cli.NewVersionCmd(cliName)) 311 command.Flags().StringVar(&listenHost, "address", env.StringFromEnv("ARGOCD_SERVER_LISTEN_ADDRESS", common.DefaultAddressAPIServer), "Listen on given address") 312 command.Flags().IntVar(&listenPort, "port", common.DefaultPortAPIServer, "Listen on given port") 313 command.Flags().StringVar(&metricsHost, env.StringFromEnv("ARGOCD_SERVER_METRICS_LISTEN_ADDRESS", "metrics-address"), common.DefaultAddressAPIServerMetrics, "Listen for metrics on given address") 314 command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port") 315 command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to") 316 command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode") 317 command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_SERVER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)") 318 command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)") 319 command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.") 320 command.Flags().StringVar(&frameOptions, "x-frame-options", env.StringFromEnv("ARGOCD_SERVER_X_FRAME_OPTIONS", "sameorigin"), "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".") 321 command.Flags().StringVar(&contentSecurityPolicy, "content-security-policy", env.StringFromEnv("ARGOCD_SERVER_CONTENT_SECURITY_POLICY", "frame-ancestors 'self';"), "Set Content-Security-Policy header in HTTP responses to `value`. To disable, set to \"\".") 322 command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to repository server") 323 command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to repo server") 324 command.Flags().BoolVar(&dexServerPlaintext, "dex-server-plaintext", env.ParseBoolFromEnv("ARGOCD_SERVER_DEX_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to dex server") 325 command.Flags().BoolVar(&dexServerStrictTLS, "dex-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_DEX_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to dex server") 326 command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces where application resources can be managed in") 327 command.Flags().BoolVar(&enableProxyExtension, "enable-proxy-extension", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_PROXY_EXTENSION", false), "Enable Proxy Extension feature") 328 command.Flags().IntVar(&webhookParallelism, "webhook-parallelism-limit", env.ParseNumFromEnv("ARGOCD_SERVER_WEBHOOK_PARALLELISM_LIMIT", 50, 1, 1000), "Number of webhook requests processed concurrently") 329 command.Flags().StringSliceVar(&enableK8sEvent, "enable-k8s-event", env.StringsFromEnv("ARGOCD_ENABLE_K8S_EVENT", argo.DefaultEnableEventList(), ","), "Enable ArgoCD to use k8s event. For disabling all events, set the value as `none`. (e.g --enable-k8s-event=none), For enabling specific events, set the value as `event reason`. (e.g --enable-k8s-event=StatusRefreshed,ResourceCreated)") 330 command.Flags().BoolVar(&hydratorEnabled, "hydrator-enabled", env.ParseBoolFromEnv("ARGOCD_HYDRATOR_ENABLED", false), "Feature flag to enable Hydrator. Default (\"false\")") 331 command.Flags().BoolVar(&syncWithReplaceAllowed, "sync-with-replace-allowed", env.ParseBoolFromEnv("ARGOCD_SYNC_WITH_REPLACE_ALLOWED", true), "Whether to allow users to select replace for syncs from UI/CLI") 332 333 // Flags related to the applicationSet component. 334 command.Flags().StringVar(&scmRootCAPath, "appset-scm-root-ca-path", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH", ""), "Provide Root CA Path for self-signed TLS Certificates") 335 command.Flags().BoolVar(&enableScmProviders, "appset-enable-scm-providers", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS", true), "Enable retrieving information from SCM providers, used by the SCM and PR generators (Default: true)") 336 command.Flags().StringSliceVar(&allowedScmProviders, "appset-allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed custom SCM provider API URLs. This restriction does not apply to SCM or PR generators which do not accept a custom API URL. (Default: Empty = all)") 337 command.Flags().BoolVar(&enableNewGitFileGlobbing, "appset-enable-new-git-file-globbing", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING", false), "Enable new globbing in Git files generator.") 338 command.Flags().BoolVar(&enableGitHubAPIMetrics, "appset-enable-github-api-metrics", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_GITHUB_API_METRICS", false), "Enable GitHub API metrics for generators that use the GitHub API") 339 340 tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command) 341 cacheSrc = servercache.AddCacheFlagsToCmd(command, cacheutil.Options{ 342 OnClientCreated: func(client *redis.Client) { 343 redisClient = client 344 }, 345 }) 346 repoServerCacheSrc = reposervercache.AddCacheFlagsToCmd(command, cacheutil.Options{FlagPrefix: "repo-server-"}) 347 return command 348 }