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  }