diff --git a/go.mod b/go.mod index 771fc1e18..0089b8e79 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/armon/go-metrics v0.3.10 github.com/aws/aws-sdk-go v1.44.321 github.com/cespare/xxhash/v2 v2.3.0 + github.com/coreos/go-systemd/v22 v22.5.0 github.com/cristalhq/hedgedhttp v0.9.1 github.com/davecgh/go-spew v1.1.1 github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb @@ -61,7 +62,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect diff --git a/server/server.go b/server/server.go index 7b8e7593d..0e66ad8fd 100644 --- a/server/server.go +++ b/server/server.go @@ -7,12 +7,14 @@ package server import ( "context" "crypto/tls" + "encoding/json" "flag" "fmt" "math" "net" "net/http" _ "net/http/pprof" // anonymous import to get the pprof handler registered + "os" "strconv" "strings" "time" @@ -38,6 +40,8 @@ import ( "github.com/grafana/dskit/log" "github.com/grafana/dskit/middleware" "github.com/grafana/dskit/signals" + + "github.com/coreos/go-systemd/v22/activation" ) // Listen on the named network @@ -47,6 +51,13 @@ const ( DefaultNetwork = "tcp" // NetworkTCPV4 for IPV4 only NetworkTCPV4 = "tcp4" + // NetworkSystemdListenFd for using a passed open file descriptor + NetworkSystemdListenFd = "sd_listen_fd" +) + +const ( + // See `SD_LISTEN_FDS(3)` + NetworkSystemdListenFdsStart = 3 ) // SignalHandler used by Server. @@ -248,6 +259,43 @@ func NewWithMetrics(cfg Config, metrics *Metrics) (*Server, error) { return newServer(cfg, metrics) } +func listen(network string, address string, port int, systemdListenFiles []*os.File) (net.Listener, error) { + if network == "" { + network = DefaultNetwork + } + + switch network { + case DefaultNetwork, NetworkTCPV4: + return net.Listen(network, net.JoinHostPort(address, strconv.Itoa(port))) + case NetworkSystemdListenFd: + if address == "" { + address = "LISTEN_FD_" + strconv.Itoa(NetworkSystemdListenFdsStart) + } + + listeners := map[string]net.Listener{} + for index, file := range systemdListenFiles { + if listener, err := net.FileListener(file); err == nil { + listeners[file.Name()] = listener + listeners["LISTEN_FD_"+strconv.Itoa(index+NetworkSystemdListenFdsStart)] = listener + file.Close() + } + } + + bs, _ := json.Marshal(listeners) + fmt.Println(string(bs)) + + listener, hasListener := listeners[address] + + if !hasListener { + return nil, fmt.Errorf("could not listen on 'sd_listen_fd', no file descriptor given for name '%s'", address) + } + + return listener, nil + default: + return nil, fmt.Errorf("cannot listen on unknown network '%s'", network) + } +} + func newServer(cfg Config, metrics *Metrics) (*Server, error) { // If user doesn't supply a logging implementation, by default instantiate go-kit. logger := cfg.Log @@ -260,15 +308,14 @@ func newServer(cfg Config, metrics *Metrics) (*Server, error) { gatherer = prometheus.DefaultGatherer } - network := cfg.HTTPListenNetwork - if network == "" { - network = DefaultNetwork - } + systemdListenFiles := activation.Files(true) + // Setup listeners first, so we can fail early if the port is in use. - httpListener, err := net.Listen(network, net.JoinHostPort(cfg.HTTPListenAddress, strconv.Itoa(cfg.HTTPListenPort))) + httpListener, err := listen(cfg.HTTPListenNetwork, cfg.HTTPListenAddress, cfg.HTTPListenPort, systemdListenFiles) if err != nil { return nil, err } + httpListener = middleware.CountingListener(httpListener, metrics.TCPConnections.WithLabelValues("http")) if cfg.HTTPLogClosedConnectionsWithoutResponse { httpListener = middleware.NewZeroResponseListener(httpListener, level.Warn(logger)) @@ -279,11 +326,7 @@ func newServer(cfg Config, metrics *Metrics) (*Server, error) { httpListener = netutil.LimitListener(httpListener, cfg.HTTPConnLimit) } - network = cfg.GRPCListenNetwork - if network == "" { - network = DefaultNetwork - } - grpcListener, err := net.Listen(network, net.JoinHostPort(cfg.GRPCListenAddress, strconv.Itoa(cfg.GRPCListenPort))) + grpcListener, err := listen(cfg.GRPCListenNetwork, cfg.GRPCListenAddress, cfg.GRPCListenPort, systemdListenFiles) if err != nil { return nil, err }