github.com/argoproj/argo-cd@v1.8.7/cmd/argocd-repo-server/commands/argocd_repo_server.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "math" 6 "net" 7 "net/http" 8 "os" 9 "time" 10 11 "github.com/argoproj/pkg/stats" 12 "github.com/go-redis/redis/v8" 13 log "github.com/sirupsen/logrus" 14 "github.com/spf13/cobra" 15 "google.golang.org/grpc/health/grpc_health_v1" 16 17 "github.com/argoproj/argo-cd/common" 18 "github.com/argoproj/argo-cd/reposerver" 19 "github.com/argoproj/argo-cd/reposerver/apiclient" 20 reposervercache "github.com/argoproj/argo-cd/reposerver/cache" 21 "github.com/argoproj/argo-cd/reposerver/metrics" 22 "github.com/argoproj/argo-cd/reposerver/repository" 23 cacheutil "github.com/argoproj/argo-cd/util/cache" 24 "github.com/argoproj/argo-cd/util/cli" 25 "github.com/argoproj/argo-cd/util/env" 26 "github.com/argoproj/argo-cd/util/errors" 27 "github.com/argoproj/argo-cd/util/gpg" 28 "github.com/argoproj/argo-cd/util/healthz" 29 ioutil "github.com/argoproj/argo-cd/util/io" 30 "github.com/argoproj/argo-cd/util/tls" 31 ) 32 33 const ( 34 // CLIName is the name of the CLI 35 cliName = "argocd-repo-server" 36 gnuPGSourcePath = "/app/config/gpg/source" 37 38 defaultPauseGenerationAfterFailedGenerationAttempts = 3 39 defaultPauseGenerationOnFailureForMinutes = 60 40 defaultPauseGenerationOnFailureForRequests = 0 41 ) 42 43 func getGnuPGSourcePath() string { 44 if path := os.Getenv("ARGOCD_GPG_DATA_PATH"); path != "" { 45 return path 46 } else { 47 return gnuPGSourcePath 48 } 49 } 50 51 func getPauseGenerationAfterFailedGenerationAttempts() int { 52 return env.ParseNumFromEnv(common.EnvPauseGenerationAfterFailedAttempts, defaultPauseGenerationAfterFailedGenerationAttempts, 0, math.MaxInt32) 53 } 54 55 func getPauseGenerationOnFailureForMinutes() int { 56 return env.ParseNumFromEnv(common.EnvPauseGenerationMinutes, defaultPauseGenerationOnFailureForMinutes, 0, math.MaxInt32) 57 } 58 59 func getPauseGenerationOnFailureForRequests() int { 60 return env.ParseNumFromEnv(common.EnvPauseGenerationRequests, defaultPauseGenerationOnFailureForRequests, 0, math.MaxInt32) 61 } 62 63 func NewCommand() *cobra.Command { 64 var ( 65 logFormat string 66 logLevel string 67 parallelismLimit int64 68 listenPort int 69 metricsPort int 70 cacheSrc func() (*reposervercache.Cache, error) 71 tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error) 72 redisClient *redis.Client 73 ) 74 var command = cobra.Command{ 75 Use: cliName, 76 Short: "Run ArgoCD Repository Server", 77 Long: "ArgoCD Repository Server is an internal service which maintains a local cache of the Git repository holding the application manifests, and is responsible for generating and returning the Kubernetes manifests. This command runs Repository Server in the foreground. It can be configured by following options.", 78 DisableAutoGenTag: true, 79 RunE: func(c *cobra.Command, args []string) error { 80 cli.SetLogFormat(logFormat) 81 cli.SetLogLevel(logLevel) 82 83 tlsConfigCustomizer, err := tlsConfigCustomizerSrc() 84 errors.CheckError(err) 85 86 cache, err := cacheSrc() 87 errors.CheckError(err) 88 89 metricsServer := metrics.NewMetricsServer() 90 cacheutil.CollectMetrics(redisClient, metricsServer) 91 server, err := reposerver.NewServer(metricsServer, cache, tlsConfigCustomizer, repository.RepoServerInitConstants{ 92 ParallelismLimit: parallelismLimit, 93 PauseGenerationAfterFailedGenerationAttempts: getPauseGenerationAfterFailedGenerationAttempts(), 94 PauseGenerationOnFailureForMinutes: getPauseGenerationOnFailureForMinutes(), 95 PauseGenerationOnFailureForRequests: getPauseGenerationOnFailureForRequests(), 96 }) 97 errors.CheckError(err) 98 99 grpc := server.CreateGRPC() 100 listener, err := net.Listen("tcp", fmt.Sprintf(":%d", listenPort)) 101 errors.CheckError(err) 102 103 healthz.ServeHealthCheck(http.DefaultServeMux, func(r *http.Request) error { 104 if val, ok := r.URL.Query()["full"]; ok && len(val) > 0 && val[0] == "true" { 105 // connect to itself to make sure repo server is able to serve connection 106 // used by liveness probe to auto restart repo server 107 // see https://github.com/argoproj/argo-cd/issues/5110 for more information 108 conn, err := apiclient.NewConnection(fmt.Sprintf("localhost:%d", listenPort), 60) 109 if err != nil { 110 return err 111 } 112 defer ioutil.Close(conn) 113 client := grpc_health_v1.NewHealthClient(conn) 114 res, err := client.Check(r.Context(), &grpc_health_v1.HealthCheckRequest{}) 115 if err != nil { 116 return err 117 } 118 if res.Status != grpc_health_v1.HealthCheckResponse_SERVING { 119 return fmt.Errorf("grpc health check status is '%v'", res.Status) 120 } 121 return nil 122 } 123 return nil 124 }) 125 http.Handle("/metrics", metricsServer.GetHandler()) 126 go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf(":%d", metricsPort), nil)) }() 127 128 if gpg.IsGPGEnabled() { 129 log.Infof("Initializing GnuPG keyring at %s", common.GetGnuPGHomePath()) 130 err = gpg.InitializeGnuPG() 131 errors.CheckError(err) 132 133 log.Infof("Populating GnuPG keyring with keys from %s", getGnuPGSourcePath()) 134 added, removed, err := gpg.SyncKeyRingFromDirectory(getGnuPGSourcePath()) 135 errors.CheckError(err) 136 log.Infof("Loaded %d (and removed %d) keys from keyring", len(added), len(removed)) 137 138 go func() { errors.CheckError(reposerver.StartGPGWatcher(getGnuPGSourcePath())) }() 139 } 140 141 log.Infof("argocd-repo-server %s serving on %s", common.GetVersion(), listener.Addr()) 142 stats.RegisterStackDumper() 143 stats.StartStatsTicker(10 * time.Minute) 144 stats.RegisterHeapDumper("memprofile") 145 err = grpc.Serve(listener) 146 errors.CheckError(err) 147 return nil 148 }, 149 } 150 151 command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json") 152 command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error") 153 command.Flags().Int64Var(¶llelismLimit, "parallelismlimit", 0, "Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.") 154 command.Flags().IntVar(&listenPort, "port", common.DefaultPortRepoServer, "Listen on given port for incoming connections") 155 command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortRepoServerMetrics, "Start metrics server on given port") 156 157 tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command) 158 cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) { 159 redisClient = client 160 }) 161 return &command 162 }