github.phpd.cn/thought-machine/please@v12.2.0+incompatible/tools/cache/rpc_server_main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"net/http"
     7  	_ "net/http/pprof"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/grpc-ecosystem/go-grpc-prometheus"
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	"gopkg.in/op/go-logging.v1"
    14  
    15  	"cli"
    16  	"tools/cache/cluster"
    17  	"tools/cache/server"
    18  )
    19  
    20  var log = logging.MustGetLogger("rpc_cache_server")
    21  
    22  var opts struct {
    23  	Usage     string `usage:"rpc_cache_server is a server for Please's remote RPC cache.\n\nSee https://please.build/cache.html for more information."`
    24  	Port      int    `short:"p" long:"port" description:"Port to serve on" default:"7677"`
    25  	HTTPPort  int    `short:"h" long:"http_port" description:"Port to serve HTTP on (for profiling, metrics etc)"`
    26  	Dir       string `short:"d" long:"dir" description:"Directory to write into" default:"plz-rpc-cache"`
    27  	Verbosity int    `short:"v" long:"verbosity" description:"Verbosity of output (higher number = more output, default 2 -> notice, warnings and errors only)" default:"2"`
    28  	LogFile   string `long:"log_file" description:"File to log to (in addition to stdout)"`
    29  
    30  	CleanFlags struct {
    31  		LowWaterMark   cli.ByteSize `short:"l" long:"low_water_mark" description:"Size of cache to clean down to" default:"18G"`
    32  		HighWaterMark  cli.ByteSize `short:"i" long:"high_water_mark" description:"Max size of cache to clean at" default:"20G"`
    33  		CleanFrequency cli.Duration `short:"f" long:"clean_frequency" description:"Frequency to clean cache at" default:"10m"`
    34  		MaxArtifactAge cli.Duration `short:"m" long:"max_artifact_age" description:"Clean any artifact that's not been read in this long" default:"720h"`
    35  	} `group:"Options controlling when to clean the cache"`
    36  
    37  	TLSFlags struct {
    38  		KeyFile       string `long:"key_file" description:"File containing PEM-encoded private key."`
    39  		CertFile      string `long:"cert_file" description:"File containing PEM-encoded certificate"`
    40  		CACertFile    string `long:"ca_cert_file" description:"File containing PEM-encoded CA certificate"`
    41  		WritableCerts string `long:"writable_certs" description:"File or directory containing certificates that are allowed to write to the cache"`
    42  		ReadonlyCerts string `long:"readonly_certs" description:"File or directory containing certificates that are allowed to read from the cache"`
    43  	} `group:"Options controlling TLS communication & authentication"`
    44  
    45  	ClusterFlags struct {
    46  		ClusterPort      int    `long:"cluster_port" default:"7946" description:"Port to gossip among cluster nodes on"`
    47  		ClusterAddresses string `short:"c" long:"cluster_addresses" description:"Comma-separated addresses of one or more nodes to join a cluster"`
    48  		SeedCluster      bool   `long:"seed_cluster" description:"Seeds a new cache cluster."`
    49  		ClusterSize      int    `long:"cluster_size" description:"Number of nodes to expect in the cluster.\nMust be passed if --seed_cluster is, has no effect otherwise."`
    50  		NodeName         string `long:"node_name" env:"NODE_NAME" description:"Name of this node in the cluster. Only usually needs to be passed if running multiple nodes on the same machine, when it should be unique."`
    51  		SeedIf           string `long:"seed_if" description:"Makes us the seed (overriding seed_cluster) if node_name matches this value and we can't resolve any cluster addresses. This makes it a lot easier to set up in automated deployments like Kubernetes."`
    52  		AdvertiseAddr    string `long:"advertise_addr" env:"NODE_IP" description:"IP address to advertise to other cluster nodes"`
    53  	} `group:"Options controlling clustering behaviour"`
    54  }
    55  
    56  // handleHTTP implements the http.Handler interface to return some simple stats.
    57  func handleHTTP(w http.ResponseWriter, cache *server.Cache) {
    58  	w.Header().Set("Content-Type", "text/plain")
    59  	w.Header().Set("X-Clacks-Overhead", "GNU Terry Pratchett")
    60  	w.Write([]byte(fmt.Sprintf("Total size: %d bytes\nNum files: %d\n", cache.TotalSize(), cache.NumFiles())))
    61  }
    62  
    63  // serveHTTP serves HTTP responses (including metrics) on the given port.
    64  func serveHTTP(port int, cache *server.Cache) {
    65  	mux := http.NewServeMux()
    66  	mux.Handle("/metrics", prometheus.Handler())
    67  	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { handleHTTP(w, cache) })
    68  	s := &http.Server{
    69  		Addr:    fmt.Sprintf(":%d", port),
    70  		Handler: mux,
    71  	}
    72  	go func() {
    73  		if opts.TLSFlags.KeyFile != "" {
    74  			log.Fatalf("%s\n", s.ListenAndServeTLS(opts.TLSFlags.CertFile, opts.TLSFlags.KeyFile))
    75  		} else {
    76  			log.Fatalf("%s\n", s.ListenAndServe())
    77  		}
    78  	}()
    79  	log.Notice("Serving HTTP stats on port %d", port)
    80  }
    81  
    82  func main() {
    83  	cli.ParseFlagsOrDie("Please RPC cache server", "5.5.0", &opts)
    84  	cli.InitLogging(opts.Verbosity)
    85  	if opts.LogFile != "" {
    86  		cli.InitFileLogging(opts.LogFile, opts.Verbosity)
    87  	}
    88  	if (opts.TLSFlags.KeyFile == "") != (opts.TLSFlags.CertFile == "") {
    89  		log.Fatalf("Must pass both --key_file and --cert_file if you pass one")
    90  	} else if opts.TLSFlags.KeyFile == "" && (opts.TLSFlags.WritableCerts != "" || opts.TLSFlags.ReadonlyCerts != "") {
    91  		log.Fatalf("You can only use --writable_certs / --readonly_certs with https (--key_file and --cert_file)")
    92  	}
    93  
    94  	log.Notice("Scanning existing cache directory %s...", opts.Dir)
    95  	cache := server.NewCache(opts.Dir, time.Duration(opts.CleanFlags.CleanFrequency),
    96  		time.Duration(opts.CleanFlags.MaxArtifactAge),
    97  		uint64(opts.CleanFlags.LowWaterMark), uint64(opts.CleanFlags.HighWaterMark))
    98  
    99  	var clusta *cluster.Cluster
   100  	if opts.ClusterFlags.SeedIf != "" && opts.ClusterFlags.SeedIf == opts.ClusterFlags.NodeName {
   101  		ips, err := net.LookupIP(opts.ClusterFlags.ClusterAddresses)
   102  		opts.ClusterFlags.SeedCluster = err != nil || len(ips) == 0
   103  	}
   104  	if opts.ClusterFlags.SeedCluster {
   105  		if opts.ClusterFlags.ClusterSize < 2 {
   106  			log.Fatalf("You must pass a cluster size of > 1 when initialising the seed node.")
   107  		}
   108  		clusta = cluster.NewCluster(opts.ClusterFlags.ClusterPort, opts.Port, opts.ClusterFlags.NodeName, opts.ClusterFlags.AdvertiseAddr)
   109  		clusta.Init(opts.ClusterFlags.ClusterSize)
   110  	} else if opts.ClusterFlags.ClusterAddresses != "" {
   111  		clusta = cluster.NewCluster(opts.ClusterFlags.ClusterPort, opts.Port, opts.ClusterFlags.NodeName, opts.ClusterFlags.AdvertiseAddr)
   112  		clusta.Join(strings.Split(opts.ClusterFlags.ClusterAddresses, ","))
   113  	}
   114  
   115  	log.Notice("Starting up RPC cache server on port %d...", opts.Port)
   116  	s, lis := server.BuildGrpcServer(opts.Port, cache, clusta, opts.TLSFlags.KeyFile, opts.TLSFlags.CertFile,
   117  		opts.TLSFlags.CACertFile, opts.TLSFlags.ReadonlyCerts, opts.TLSFlags.WritableCerts)
   118  
   119  	grpc_prometheus.Register(s)
   120  	grpc_prometheus.EnableHandlingTimeHistogram()
   121  	if opts.HTTPPort != 0 {
   122  		go serveHTTP(opts.HTTPPort, cache)
   123  	}
   124  	server.ServeGrpcForever(s, lis)
   125  }