github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/cmd/gerrit/main.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 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 "context" 21 "errors" 22 "flag" 23 "fmt" 24 "os" 25 "runtime" 26 "strings" 27 "time" 28 29 "github.com/sirupsen/logrus" 30 31 "sigs.k8s.io/prow/pkg/config" 32 "sigs.k8s.io/prow/pkg/diskutil" 33 "sigs.k8s.io/prow/pkg/metrics" 34 "sigs.k8s.io/prow/pkg/moonraker" 35 "sigs.k8s.io/prow/pkg/pjutil/pprof" 36 37 "github.com/prometheus/client_golang/prometheus" 38 "sigs.k8s.io/prow/pkg/flagutil" 39 prowflagutil "sigs.k8s.io/prow/pkg/flagutil" 40 configflagutil "sigs.k8s.io/prow/pkg/flagutil/config" 41 "sigs.k8s.io/prow/pkg/gerrit/adapter" 42 "sigs.k8s.io/prow/pkg/interrupts" 43 "sigs.k8s.io/prow/pkg/logrusutil" 44 ) 45 46 var ( 47 diskFree = prometheus.NewGauge(prometheus.GaugeOpts{ 48 Name: "gerrit_disk_free", 49 Help: "Free gb on gerrit-cache disk.", 50 }) 51 diskUsed = prometheus.NewGauge(prometheus.GaugeOpts{ 52 Name: "gerrit_disk_used", 53 Help: "Used gb on gerrit-cache disk.", 54 }) 55 diskTotal = prometheus.NewGauge(prometheus.GaugeOpts{ 56 Name: "gerrit_disk_total", 57 Help: "Total gb on gerrit-cache disk.", 58 }) 59 diskInodeFree = prometheus.NewGauge(prometheus.GaugeOpts{ 60 Name: "gerrit_disk_inode_free", 61 Help: "Free inodes on gerrit-cache disk.", 62 }) 63 diskInodeUsed = prometheus.NewGauge(prometheus.GaugeOpts{ 64 Name: "gerrit_disk_inode_used", 65 Help: "Used inodes on gerrit-cache disk.", 66 }) 67 diskInodeTotal = prometheus.NewGauge(prometheus.GaugeOpts{ 68 Name: "gerrit_disk_inode_total", 69 Help: "Total inodes on gerrit-cache disk.", 70 }) 71 ) 72 73 func init() { 74 prometheus.MustRegister(diskFree) 75 prometheus.MustRegister(diskUsed) 76 prometheus.MustRegister(diskTotal) 77 prometheus.MustRegister(diskInodeFree) 78 prometheus.MustRegister(diskInodeUsed) 79 prometheus.MustRegister(diskInodeTotal) 80 } 81 82 type options struct { 83 cookiefilePath string 84 tokenPathOverride string 85 config configflagutil.ConfigOptions 86 // lastSyncFallback is the path to sync the latest timestamp 87 // Can be /local/path, gs://path/to/object or s3://path/to/object. 88 lastSyncFallback string 89 dryRun bool 90 kubernetes prowflagutil.KubernetesOptions 91 storage prowflagutil.StorageClientOptions 92 instrumentationOptions prowflagutil.InstrumentationOptions 93 gerrit prowflagutil.GerritOptions 94 changeWorkerPoolSize int 95 pushGatewayInterval time.Duration 96 instanceConcurrencyLimit uint 97 } 98 99 func (o *options) validate() error { 100 if o.cookiefilePath != "" && o.tokenPathOverride != "" { 101 return fmt.Errorf("only one of --cookiefile=%q --token-path=%q allowed, not both", o.cookiefilePath, o.tokenPathOverride) 102 } 103 if o.cookiefilePath == "" && o.tokenPathOverride == "" { 104 logrus.Info("--cookiefile is not set, using anonymous authentication") 105 } 106 107 for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.storage, &o.instrumentationOptions, &o.config, &o.gerrit} { 108 if err := group.Validate(o.dryRun); err != nil { 109 return err 110 } 111 } 112 113 if o.lastSyncFallback == "" { 114 return errors.New("--last-sync-fallback must be set") 115 } 116 117 if strings.HasPrefix(o.lastSyncFallback, "gs://") && !o.storage.HasGCSCredentials() { 118 logrus.WithField("last-sync-fallback", o.lastSyncFallback).Info("--gcs-credentials-file unset, will try and access with a default service account") 119 } 120 if strings.HasPrefix(o.lastSyncFallback, "s3://") && !o.storage.HasS3Credentials() { 121 logrus.WithField("last-sync-fallback", o.lastSyncFallback).Info("--s3-credentials-file unset, will try and access with auto-discovered credentials") 122 } 123 if o.changeWorkerPoolSize < 1 { 124 return errors.New("change-worker-pool-size must be at least 1") 125 } 126 return nil 127 } 128 129 func gatherOptions(fs *flag.FlagSet, args ...string) options { 130 var o options 131 fs.StringVar(&o.cookiefilePath, "cookiefile", "", "Path to git http.cookiefile, leave empty for anonymous") 132 fs.StringVar(&o.lastSyncFallback, "last-sync-fallback", "", "The /local/path, gs://path/to/object or s3://path/to/object to sync the latest timestamp") 133 fs.BoolVar(&o.dryRun, "dry-run", false, "Run in dry-run mode, performing no modifying actions.") 134 fs.StringVar(&o.tokenPathOverride, "token-path", "", "Force the use of the token in this path, use with gcloud auth print-access-token") 135 fs.IntVar(&o.changeWorkerPoolSize, "change-worker-pool-size", 1, "Number of workers processing changes for each instance.") 136 fs.DurationVar(&o.pushGatewayInterval, "push-gateway-interval", time.Minute, "Interval at which prometheus metrics for disk space are pushed.") 137 // TODO(cjwagner): remove deprecated flag. 138 fs.UintVar(&o.instanceConcurrencyLimit, "instance-concurrency-limit", 5, "[DEPRECATED] Number of concurrent calls that can be made to any single Gerrit host instance simultaneously.") 139 for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.storage, &o.instrumentationOptions, &o.config, &o.gerrit} { 140 group.AddFlags(fs) 141 } 142 fs.Parse(args) 143 return o 144 } 145 146 func main() { 147 logrusutil.ComponentInit() 148 149 o := gatherOptions(flag.NewFlagSet(os.Args[0], flag.ExitOnError), os.Args[1:]...) 150 if err := o.validate(); err != nil { 151 logrus.Fatalf("Invalid options: %v", err) 152 } 153 154 runtime.SetBlockProfileRate(100_000_000) // 0.1 second sample rate https://github.com/DataDog/go-profiler-notes/blob/main/guide/README.md#block-profiler-limitations 155 pprof.Instrument(o.instrumentationOptions) 156 157 ca, err := o.config.ConfigAgent() 158 if err != nil { 159 logrus.WithError(err).Fatal("Error starting config agent.") 160 } 161 cfg := ca.Config 162 163 // Expose Prometheus metrics 164 metrics.ExposeMetrics("gerrit", cfg().PushGateway, o.instrumentationOptions.MetricsPort) 165 166 prowJobClient, err := o.kubernetes.ProwJobClient(cfg().ProwJobNamespace, o.dryRun) 167 if err != nil { 168 logrus.WithError(err).Fatal("Error getting kube client.") 169 } 170 171 ctx := context.Background() // TODO(fejta): use something better 172 op, err := o.storage.StorageClient(ctx) 173 if err != nil { 174 logrus.WithError(err).Fatal("Error creating opener") 175 } 176 177 persist := false 178 if o.config.InRepoConfigCacheDirBase != "" { 179 persist = true 180 } 181 182 var ircg config.InRepoConfigGetter 183 if o.config.MoonrakerAddress != "" { 184 moonrakerClient, err := moonraker.NewClient(o.config.MoonrakerAddress, ca) 185 if err != nil { 186 logrus.WithError(err).Fatal("Error getting Moonraker client.") 187 } 188 ircg = moonrakerClient 189 } else { 190 gitClient, err := (&prowflagutil.GitHubOptions{}).GitClientFactory(o.cookiefilePath, &o.config.InRepoConfigCacheDirBase, o.dryRun, persist) 191 if err != nil { 192 logrus.WithError(err).Fatal("Error creating git client.") 193 } 194 195 if o.config.InRepoConfigCacheDirBase != "" { 196 go diskMonitor(o.pushGatewayInterval, o.config.InRepoConfigCacheDirBase) 197 } 198 199 ircc, err := config.NewInRepoConfigCache(o.config.InRepoConfigCacheSize, ca, gitClient) 200 if err != nil { 201 logrus.WithError(err).Fatal("Error creating InRepoConfigCache.") 202 } 203 ircg = ircc 204 } 205 c := adapter.NewController(ctx, prowJobClient, op, ca, o.cookiefilePath, o.tokenPathOverride, o.lastSyncFallback, o.changeWorkerPoolSize, o.gerrit.MaxQPS, o.gerrit.MaxBurst, ircg) 206 207 logrus.Infof("Starting gerrit fetcher") 208 209 defer interrupts.WaitForGracefulShutdown() 210 interrupts.Tick(func() { 211 c.Sync() 212 }, func() time.Duration { 213 return cfg().Gerrit.TickInterval.Duration 214 }) 215 } 216 217 // helper to update disk metrics (copied from ghproxy) 218 func diskMonitor(interval time.Duration, diskRoot string) { 219 logger := logrus.WithField("sync-loop", "disk-monitor") 220 ticker := time.NewTicker(interval) 221 for ; true; <-ticker.C { 222 logger.Info("tick") 223 _, bytesFree, bytesUsed, _, inodesFree, inodesUsed, err := diskutil.GetDiskUsage(diskRoot) 224 if err != nil { 225 logger.WithError(err).Error("Failed to get disk metrics") 226 } else { 227 diskFree.Set(float64(bytesFree) / 1e9) 228 diskUsed.Set(float64(bytesUsed) / 1e9) 229 diskTotal.Set(float64(bytesFree+bytesUsed) / 1e9) 230 diskInodeFree.Set(float64(inodesFree)) 231 diskInodeUsed.Set(float64(inodesUsed)) 232 diskInodeTotal.Set(float64(inodesFree + inodesUsed)) 233 } 234 } 235 }