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 }