TUN-528: Move cloudflared into a separate repo

This commit is contained in:
Areg Harutyunyan
2018-05-01 18:45:06 -05:00
parent e8c621a648
commit d06fc520c7
4726 changed files with 1763680 additions and 0 deletions

18
vendor/github.com/facebookgo/grace/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,18 @@
language: go
go:
- 1.7
before_install:
- go get -v github.com/golang/lint/golint
install:
- go install -race -v std
- go get -race -t -v ./...
- go install -race -v ./...
script:
- go vet ./...
- $HOME/gopath/bin/golint .
- go test -cpu=2 -race -v ./...
- go test -cpu=2 -covermode=atomic ./...

50
vendor/github.com/facebookgo/grace/gracedemo/demo.go generated vendored Normal file
View File

@@ -0,0 +1,50 @@
// Command gracedemo implements a demo server showing how to gracefully
// terminate an HTTP server using grace.
package main
import (
"flag"
"fmt"
"net/http"
"os"
"time"
"github.com/facebookgo/grace/gracehttp"
)
var (
address0 = flag.String("a0", ":48567", "Zero address to bind to.")
address1 = flag.String("a1", ":48568", "First address to bind to.")
address2 = flag.String("a2", ":48569", "Second address to bind to.")
now = time.Now()
)
func main() {
flag.Parse()
gracehttp.Serve(
&http.Server{Addr: *address0, Handler: newHandler("Zero ")},
&http.Server{Addr: *address1, Handler: newHandler("First ")},
&http.Server{Addr: *address2, Handler: newHandler("Second")},
)
}
func newHandler(name string) http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/sleep/", func(w http.ResponseWriter, r *http.Request) {
duration, err := time.ParseDuration(r.FormValue("duration"))
if err != nil {
http.Error(w, err.Error(), 400)
return
}
time.Sleep(duration)
fmt.Fprintf(
w,
"%s started at %s slept for %d nanoseconds from pid %d.\n",
name,
now,
duration.Nanoseconds(),
os.Getpid(),
)
})
return mux
}

220
vendor/github.com/facebookgo/grace/gracehttp/http.go generated vendored Normal file
View File

@@ -0,0 +1,220 @@
// Package gracehttp provides easy to use graceful restart
// functionality for HTTP server.
package gracehttp
import (
"bytes"
"crypto/tls"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"github.com/facebookgo/grace/gracenet"
"github.com/facebookgo/httpdown"
)
var (
logger *log.Logger
didInherit = os.Getenv("LISTEN_FDS") != ""
ppid = os.Getppid()
)
type option func(*app)
// An app contains one or more servers and associated configuration.
type app struct {
servers []*http.Server
http *httpdown.HTTP
net *gracenet.Net
listeners []net.Listener
sds []httpdown.Server
preStartProcess func() error
errors chan error
}
func newApp(servers []*http.Server) *app {
return &app{
servers: servers,
http: &httpdown.HTTP{},
net: &gracenet.Net{},
listeners: make([]net.Listener, 0, len(servers)),
sds: make([]httpdown.Server, 0, len(servers)),
preStartProcess: func() error { return nil },
// 2x num servers for possible Close or Stop errors + 1 for possible
// StartProcess error.
errors: make(chan error, 1+(len(servers)*2)),
}
}
func (a *app) listen() error {
for _, s := range a.servers {
// TODO: default addresses
l, err := a.net.Listen("tcp", s.Addr)
if err != nil {
return err
}
if s.TLSConfig != nil {
l = tls.NewListener(l, s.TLSConfig)
}
a.listeners = append(a.listeners, l)
}
return nil
}
func (a *app) serve() {
for i, s := range a.servers {
a.sds = append(a.sds, a.http.Serve(s, a.listeners[i]))
}
}
func (a *app) wait() {
var wg sync.WaitGroup
wg.Add(len(a.sds) * 2) // Wait & Stop
go a.signalHandler(&wg)
for _, s := range a.sds {
go func(s httpdown.Server) {
defer wg.Done()
if err := s.Wait(); err != nil {
a.errors <- err
}
}(s)
}
wg.Wait()
}
func (a *app) term(wg *sync.WaitGroup) {
for _, s := range a.sds {
go func(s httpdown.Server) {
defer wg.Done()
if err := s.Stop(); err != nil {
a.errors <- err
}
}(s)
}
}
func (a *app) signalHandler(wg *sync.WaitGroup) {
ch := make(chan os.Signal, 10)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)
for {
sig := <-ch
switch sig {
case syscall.SIGINT, syscall.SIGTERM:
// this ensures a subsequent INT/TERM will trigger standard go behaviour of
// terminating.
signal.Stop(ch)
a.term(wg)
return
case syscall.SIGUSR2:
err := a.preStartProcess()
if err != nil {
a.errors <- err
}
// we only return here if there's an error, otherwise the new process
// will send us a TERM when it's ready to trigger the actual shutdown.
if _, err := a.net.StartProcess(); err != nil {
a.errors <- err
}
}
}
}
func (a *app) run() error {
// Acquire Listeners
if err := a.listen(); err != nil {
return err
}
// Some useful logging.
if logger != nil {
if didInherit {
if ppid == 1 {
logger.Printf("Listening on init activated %s", pprintAddr(a.listeners))
} else {
const msg = "Graceful handoff of %s with new pid %d and old pid %d"
logger.Printf(msg, pprintAddr(a.listeners), os.Getpid(), ppid)
}
} else {
const msg = "Serving %s with pid %d"
logger.Printf(msg, pprintAddr(a.listeners), os.Getpid())
}
}
// Start serving.
a.serve()
// Close the parent if we inherited and it wasn't init that started us.
if didInherit && ppid != 1 {
if err := syscall.Kill(ppid, syscall.SIGTERM); err != nil {
return fmt.Errorf("failed to close parent: %s", err)
}
}
waitdone := make(chan struct{})
go func() {
defer close(waitdone)
a.wait()
}()
select {
case err := <-a.errors:
if err == nil {
panic("unexpected nil error")
}
return err
case <-waitdone:
if logger != nil {
logger.Printf("Exiting pid %d.", os.Getpid())
}
return nil
}
}
// ServeWithOptions does the same as Serve, but takes a set of options to
// configure the app struct.
func ServeWithOptions(servers []*http.Server, options ...option) error {
a := newApp(servers)
for _, opt := range options {
opt(a)
}
return a.run()
}
// Serve will serve the given http.Servers and will monitor for signals
// allowing for graceful termination (SIGTERM) or restart (SIGUSR2).
func Serve(servers ...*http.Server) error {
a := newApp(servers)
return a.run()
}
// PreStartProcess configures a callback to trigger during graceful restart
// directly before starting the successor process. This allows the current
// process to release holds on resources that the new process will need.
func PreStartProcess(hook func() error) option {
return func(a *app) {
a.preStartProcess = hook
}
}
// Used for pretty printing addresses.
func pprintAddr(listeners []net.Listener) []byte {
var out bytes.Buffer
for i, l := range listeners {
if i != 0 {
fmt.Fprint(&out, ", ")
}
fmt.Fprint(&out, l.Addr())
}
return out.Bytes()
}
// SetLogger sets logger to be able to grab some useful logs
func SetLogger(l *log.Logger) {
logger = l
}

View File

@@ -0,0 +1,313 @@
package gracehttp_test
import (
"bufio"
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"io"
"net"
"net/http"
"os"
"os/exec"
"strconv"
"sync"
"syscall"
"testing"
"time"
"github.com/facebookgo/freeport"
)
const (
testPreStartProcess = iota
)
// Debug logging.
var debugLog = flag.Bool("debug", false, "enable debug logging")
func debug(format string, a ...interface{}) {
if *debugLog {
println(fmt.Sprintf(format, a...))
}
}
// State for the test run.
type harness struct {
T *testing.T // The test instance.
httpAddr string // The address for the http server.
httpsAddr string // The address for the https server.
Process []*os.Process // The server commands, oldest to newest.
ProcessMutex sync.Mutex // The mutex to guard Process manipulation.
RequestWaitGroup sync.WaitGroup // The wait group for the HTTP requests.
newProcess chan bool // A bool is sent on start/restart.
requestCount int
requestCountMutex sync.Mutex
serveOption int
}
// Find 3 free ports and setup addresses.
func (h *harness) setupAddr() {
port, err := freeport.Get()
if err != nil {
h.T.Fatalf("Failed to find a free port: %s", err)
}
h.httpAddr = fmt.Sprintf("127.0.0.1:%d", port)
port, err = freeport.Get()
if err != nil {
h.T.Fatalf("Failed to find a free port: %s", err)
}
h.httpsAddr = fmt.Sprintf("127.0.0.1:%d", port)
debug("Addresses %s & %s", h.httpAddr, h.httpsAddr)
}
// Start a fresh server and wait for pid updates on restart.
func (h *harness) Start() {
h.setupAddr()
cmd := exec.Command(os.Args[0], "-http", h.httpAddr, "-https", h.httpsAddr, "-testOption", strconv.Itoa(h.serveOption))
stderr, err := cmd.StderrPipe()
if err != nil {
h.T.Fatal(err)
}
go func() {
reader := bufio.NewReader(stderr)
for {
line, isPrefix, err := reader.ReadLine()
if err == io.EOF {
return
}
if err != nil {
println(fmt.Sprintf("Failed to read line from server process: %s", err))
}
if isPrefix {
println(fmt.Sprintf("Deal with isPrefix for line: %s", line))
}
res := &response{}
err = json.Unmarshal([]byte(line), res)
if err != nil {
println(fmt.Sprintf("Could not parse json from stderr %s: %s", line, err))
}
if res.Error != "" {
println(fmt.Sprintf("Got error from process: %v", res))
}
process, err := os.FindProcess(res.Pid)
if err != nil {
println(fmt.Sprintf("Could not find process with pid: %d", res.Pid))
}
h.ProcessMutex.Lock()
h.Process = append(h.Process, process)
h.ProcessMutex.Unlock()
h.newProcess <- true
}
}()
err = cmd.Start()
if err != nil {
h.T.Fatalf("Failed to start command: %s", err)
}
<-h.newProcess
}
// Restart the most recent server.
func (h *harness) Restart() {
err := h.MostRecentProcess().Signal(syscall.SIGUSR2)
if err != nil {
h.T.Fatalf("Failed to send SIGUSR2 and restart process: %s", err)
}
<-h.newProcess
}
// Graceful termination of the most recent server.
func (h *harness) Stop() {
err := h.MostRecentProcess().Signal(syscall.SIGTERM)
if err != nil {
h.T.Fatalf("Failed to send SIGTERM and stop process: %s", err)
}
}
// Returns the most recent server process.
func (h *harness) MostRecentProcess() *os.Process {
h.ProcessMutex.Lock()
defer h.ProcessMutex.Unlock()
l := len(h.Process)
if l == 0 {
h.T.Fatalf("Most recent command requested before command was created.")
}
return h.Process[l-1]
}
// Get the global request count and increment it.
func (h *harness) RequestCount() int {
h.requestCountMutex.Lock()
defer h.requestCountMutex.Unlock()
c := h.requestCount
h.requestCount++
return c
}
// Helper for sending a single request.
func (h *harness) SendOne(dialgroup *sync.WaitGroup, url string, pid int) {
defer h.RequestWaitGroup.Done()
count := h.RequestCount()
debug("Send %02d pid=%d url=%s", count, pid, url)
client := &http.Client{
Transport: &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
defer func() {
time.Sleep(50 * time.Millisecond)
dialgroup.Done()
}()
return net.Dial(network, addr)
},
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
r, err := client.Get(url)
if err != nil {
h.T.Fatalf("Failed request %02d to %s pid=%d: %s", count, url, pid, err)
}
debug("Body %02d pid=%d url=%s", count, pid, url)
defer r.Body.Close()
res := &response{}
err = json.NewDecoder(r.Body).Decode(res)
if err != nil {
h.T.Fatalf("Failed to ready decode json response body pid=%d: %s", pid, err)
}
if pid != res.Pid {
for _, old := range h.Process[0 : len(h.Process)-1] {
if res.Pid == old.Pid {
h.T.Logf("Found old pid %d, ignoring the discrepancy", res.Pid)
return
}
}
h.T.Fatalf("Didn't get expected pid %d instead got %d", pid, res.Pid)
}
debug("Done %02d pid=%d url=%s", count, pid, url)
}
// Send test HTTP request.
func (h *harness) SendRequest() {
pid := h.MostRecentProcess().Pid
httpFastURL := fmt.Sprintf("http://%s/sleep/?duration=0", h.httpAddr)
httpSlowURL := fmt.Sprintf("http://%s/sleep/?duration=2s", h.httpAddr)
httpsFastURL := fmt.Sprintf("https://%s/sleep/?duration=0", h.httpsAddr)
httpsSlowURL := fmt.Sprintf("https://%s/sleep/?duration=2s", h.httpsAddr)
var dialgroup sync.WaitGroup
h.RequestWaitGroup.Add(4)
dialgroup.Add(4)
go h.SendOne(&dialgroup, httpFastURL, pid)
go h.SendOne(&dialgroup, httpSlowURL, pid)
go h.SendOne(&dialgroup, httpsFastURL, pid)
go h.SendOne(&dialgroup, httpsSlowURL, pid)
debug("Added Requests pid=%d", pid)
dialgroup.Wait()
debug("Dialed Requests pid=%d", pid)
}
// Wait for everything.
func (h *harness) Wait() {
h.RequestWaitGroup.Wait()
}
func newHarness(t *testing.T) *harness {
return &harness{
T: t,
newProcess: make(chan bool),
serveOption: -1,
}
}
// The main test case.
func TestComplex(t *testing.T) {
t.Parallel()
debug("Started TestComplex")
h := newHarness(t)
debug("Initial Start")
h.Start()
debug("Send Request 1")
h.SendRequest()
debug("Restart 1")
h.Restart()
debug("Send Request 2")
h.SendRequest()
debug("Restart 2")
h.Restart()
debug("Send Request 3")
h.SendRequest()
debug("Stopping")
h.Stop()
debug("Waiting")
h.Wait()
}
func TestComplexAgain(t *testing.T) {
t.Parallel()
debug("Started TestComplex")
h := newHarness(t)
debug("Initial Start")
h.Start()
debug("Send Request 1")
h.SendRequest()
debug("Restart 1")
h.Restart()
debug("Send Request 2")
h.SendRequest()
debug("Restart 2")
h.Restart()
debug("Send Request 3")
h.SendRequest()
debug("Stopping")
h.Stop()
debug("Waiting")
h.Wait()
}
func TestPreStartProcess(t *testing.T) {
t.Parallel()
debug("Started TestPreStartProcess")
h := newHarness(t)
h.serveOption = testPreStartProcess
debug("Initial Start")
h.Start()
debug("Send Request 1")
h.SendRequest()
debug("Restart 1")
h.Restart()
debug("Send Request 2")
h.SendRequest()
debug("Restart 2")
h.Restart()
debug("Send Request 3")
h.SendRequest()
debug("Stopping")
h.Stop()
debug("Waiting")
h.Wait()
}
func TestPreStartProcessAgain(t *testing.T) {
t.Parallel()
debug("Started TestPreStartProcessAgain")
h := newHarness(t)
h.serveOption = testPreStartProcess
debug("Initial Start")
h.Start()
debug("Send Request 1")
h.SendRequest()
debug("Restart 1")
h.Restart()
debug("Send Request 2")
h.SendRequest()
debug("Restart 2")
h.Restart()
debug("Send Request 3")
h.SendRequest()
debug("Stopping")
h.Stop()
debug("Waiting")
h.Wait()
}

View File

@@ -0,0 +1,226 @@
package gracehttp_test
import (
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"strings"
"sync"
"testing"
"time"
"github.com/facebookgo/grace/gracehttp"
)
const preStartProcessEnv = "GRACEHTTP_PRE_START_PROCESS"
func TestMain(m *testing.M) {
const (
testbinKey = "GRACEHTTP_TEST_BIN"
testbinValue = "1"
)
if os.Getenv(testbinKey) == testbinValue {
testbinMain()
return
}
if err := os.Setenv(testbinKey, testbinValue); err != nil {
panic(err)
}
os.Exit(m.Run())
}
type response struct {
Sleep time.Duration
Pid int
Error string `json:",omitempty"`
}
// Wait for 10 consecutive responses from our own pid.
//
// This prevents flaky tests that arise from the fact that we have the
// perfectly acceptable (read: not a bug) condition where both the new and the
// old servers are accepting requests. In fact the amount of time both are
// accepting at the same time and the number of requests that flip flop between
// them is unbounded and in the hands of the various kernels our code tends to
// run on.
//
// In order to combat this, we wait for 10 successful responses from our own
// pid. This is a somewhat reliable way to ensure the old server isn't
// serving anymore.
func wait(wg *sync.WaitGroup, url string) {
var success int
defer wg.Done()
for {
res, err := http.Get(url)
if err == nil {
// ensure it isn't a response from a previous instance
defer res.Body.Close()
var r response
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
log.Fatalf("Error decoding json: %s", err)
}
if r.Pid == os.Getpid() {
success++
if success == 10 {
return
}
continue
}
} else {
success = 0
// we expect connection refused
if !strings.HasSuffix(err.Error(), "connection refused") {
e2 := json.NewEncoder(os.Stderr).Encode(&response{
Error: err.Error(),
Pid: os.Getpid(),
})
if e2 != nil {
log.Fatalf("Error writing error json: %s", e2)
}
}
}
}
}
func httpsServer(addr string) *http.Server {
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
log.Fatalf("error loading cert: %v", err)
}
return &http.Server{
Addr: addr,
Handler: newHandler(),
TLSConfig: &tls.Config{
NextProtos: []string{"http/1.1"},
Certificates: []tls.Certificate{cert},
},
}
}
func testbinMain() {
var httpAddr, httpsAddr string
var testOption int
flag.StringVar(&httpAddr, "http", ":48560", "http address to bind to")
flag.StringVar(&httpsAddr, "https", ":48561", "https address to bind to")
flag.IntVar(&testOption, "testOption", -1, "which option to test on ServeWithOptions")
flag.Parse()
// we have self signed certs
http.DefaultTransport = &http.Transport{
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
// print json to stderr once we can successfully connect to all three
// addresses. the ensures we only print the line once we're ready to serve.
go func() {
var wg sync.WaitGroup
wg.Add(2)
go wait(&wg, fmt.Sprintf("http://%s/sleep/?duration=1ms", httpAddr))
go wait(&wg, fmt.Sprintf("https://%s/sleep/?duration=1ms", httpsAddr))
wg.Wait()
err := json.NewEncoder(os.Stderr).Encode(&response{Pid: os.Getpid()})
if err != nil {
log.Fatalf("Error writing startup json: %s", err)
}
}()
servers := []*http.Server{
&http.Server{Addr: httpAddr, Handler: newHandler()},
httpsServer(httpsAddr),
}
if testOption == -1 {
err := gracehttp.Serve(servers...)
if err != nil {
log.Fatalf("Error in gracehttp.Serve: %s", err)
}
} else {
if testOption == testPreStartProcess {
switch os.Getenv(preStartProcessEnv) {
case "":
err := os.Setenv(preStartProcessEnv, "READY")
if err != nil {
log.Fatalf("testbin (first incarnation) could not set %v to 'ready': %v", preStartProcessEnv, err)
}
case "FIRED":
// all good, reset for next round
err := os.Setenv(preStartProcessEnv, "READY")
if err != nil {
log.Fatalf("testbin (second incarnation) could not reset %v to 'ready': %v", preStartProcessEnv, err)
}
case "READY":
log.Fatalf("failure to update startup hook before new process started")
default:
log.Fatalf("something strange happened with %v: it ended up as %v, which is not '', 'FIRED', or 'READY'", preStartProcessEnv, os.Getenv(preStartProcessEnv))
}
err := gracehttp.ServeWithOptions(
servers,
gracehttp.PreStartProcess(func() error {
err := os.Setenv(preStartProcessEnv, "FIRED")
if err != nil {
log.Fatalf("startup hook could not set %v to 'fired': %v", preStartProcessEnv, err)
}
return nil
}),
)
if err != nil {
log.Fatalf("Error in gracehttp.Serve: %s", err)
}
}
}
}
func newHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/sleep/", func(w http.ResponseWriter, r *http.Request) {
duration, err := time.ParseDuration(r.FormValue("duration"))
if err != nil {
http.Error(w, err.Error(), 400)
}
time.Sleep(duration)
err = json.NewEncoder(w).Encode(&response{
Sleep: duration,
Pid: os.Getpid(),
})
if err != nil {
log.Fatalf("Error encoding json: %s", err)
}
})
return mux
}
// localhostCert is a PEM-encoded TLS cert with SAN IPs
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
// of ASN.1 time).
// generated from src/pkg/crypto/tls:
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBALyCfqwwip8BvTKgVKGdmjZTU8DD
ndR+WALmFPIRqn89bOU3s30olKiqYEju/SFoEvMyFRT/TWEhXHDaufThqaMCAwEA
AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAr/09uy108p51rheIOSnz4zgduyTl
M+4AmRo8/U1twEZLgfAGG/GZjREv2y4mCEUIM3HebCAqlA5jpRg76Rf8jw==
-----END CERTIFICATE-----`)
// localhostKey is the private key for localhostCert.
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBALyCfqwwip8BvTKgVKGdmjZTU8DDndR+WALmFPIRqn89bOU3s30o
lKiqYEju/SFoEvMyFRT/TWEhXHDaufThqaMCAwEAAQJAPXuWUxTV8XyAt8VhNQER
LgzJcUKb9JVsoS1nwXgPksXnPDKnL9ax8VERrdNr+nZbj2Q9cDSXBUovfdtehcdP
qQIhAO48ZsPylbTrmtjDEKiHT2Ik04rLotZYS2U873J6I7WlAiEAypDjYxXyafv/
Yo1pm9onwcetQKMW8CS3AjuV9Axzj6cCIEx2Il19fEMG4zny0WPlmbrcKvD/DpJQ
4FHrzsYlIVTpAiAas7S1uAvneqd0l02HlN9OxQKKlbUNXNme+rnOnOGS2wIgS0jW
zl1jvrOSJeP1PpAHohWz6LOhEr8uvltWkN6x3vE=
-----END RSA PRIVATE KEY-----`)

252
vendor/github.com/facebookgo/grace/gracenet/net.go generated vendored Normal file
View File

@@ -0,0 +1,252 @@
// Package gracenet provides a family of Listen functions that either open a
// fresh connection or provide an inherited connection from when the process
// was started. The behave like their counterparts in the net package, but
// transparently provide support for graceful restarts without dropping
// connections. This is provided in a systemd socket activation compatible form
// to allow using socket activation.
//
// BUG: Doesn't handle closing of listeners.
package gracenet
import (
"fmt"
"net"
"os"
"os/exec"
"strconv"
"strings"
"sync"
)
const (
// Used to indicate a graceful restart in the new process.
envCountKey = "LISTEN_FDS"
envCountKeyPrefix = envCountKey + "="
)
// In order to keep the working directory the same as when we started we record
// it at startup.
var originalWD, _ = os.Getwd()
// Net provides the family of Listen functions and maintains the associated
// state. Typically you will have only once instance of Net per application.
type Net struct {
inherited []net.Listener
active []net.Listener
mutex sync.Mutex
inheritOnce sync.Once
// used in tests to override the default behavior of starting from fd 3.
fdStart int
}
func (n *Net) inherit() error {
var retErr error
n.inheritOnce.Do(func() {
n.mutex.Lock()
defer n.mutex.Unlock()
countStr := os.Getenv(envCountKey)
if countStr == "" {
return
}
count, err := strconv.Atoi(countStr)
if err != nil {
retErr = fmt.Errorf("found invalid count value: %s=%s", envCountKey, countStr)
return
}
// In tests this may be overridden.
fdStart := n.fdStart
if fdStart == 0 {
// In normal operations if we are inheriting, the listeners will begin at
// fd 3.
fdStart = 3
}
for i := fdStart; i < fdStart+count; i++ {
file := os.NewFile(uintptr(i), "listener")
l, err := net.FileListener(file)
if err != nil {
file.Close()
retErr = fmt.Errorf("error inheriting socket fd %d: %s", i, err)
return
}
if err := file.Close(); err != nil {
retErr = fmt.Errorf("error closing inherited socket fd %d: %s", i, err)
return
}
n.inherited = append(n.inherited, l)
}
})
return retErr
}
// Listen announces on the local network address laddr. The network net must be
// a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It
// returns an inherited net.Listener for the matching network and address, or
// creates a new one using net.Listen.
func (n *Net) Listen(nett, laddr string) (net.Listener, error) {
switch nett {
default:
return nil, net.UnknownNetworkError(nett)
case "tcp", "tcp4", "tcp6":
addr, err := net.ResolveTCPAddr(nett, laddr)
if err != nil {
return nil, err
}
return n.ListenTCP(nett, addr)
case "unix", "unixpacket", "invalid_unix_net_for_test":
addr, err := net.ResolveUnixAddr(nett, laddr)
if err != nil {
return nil, err
}
return n.ListenUnix(nett, addr)
}
}
// ListenTCP announces on the local network address laddr. The network net must
// be: "tcp", "tcp4" or "tcp6". It returns an inherited net.Listener for the
// matching network and address, or creates a new one using net.ListenTCP.
func (n *Net) ListenTCP(nett string, laddr *net.TCPAddr) (*net.TCPListener, error) {
if err := n.inherit(); err != nil {
return nil, err
}
n.mutex.Lock()
defer n.mutex.Unlock()
// look for an inherited listener
for i, l := range n.inherited {
if l == nil { // we nil used inherited listeners
continue
}
if isSameAddr(l.Addr(), laddr) {
n.inherited[i] = nil
n.active = append(n.active, l)
return l.(*net.TCPListener), nil
}
}
// make a fresh listener
l, err := net.ListenTCP(nett, laddr)
if err != nil {
return nil, err
}
n.active = append(n.active, l)
return l, nil
}
// ListenUnix announces on the local network address laddr. The network net
// must be a: "unix" or "unixpacket". It returns an inherited net.Listener for
// the matching network and address, or creates a new one using net.ListenUnix.
func (n *Net) ListenUnix(nett string, laddr *net.UnixAddr) (*net.UnixListener, error) {
if err := n.inherit(); err != nil {
return nil, err
}
n.mutex.Lock()
defer n.mutex.Unlock()
// look for an inherited listener
for i, l := range n.inherited {
if l == nil { // we nil used inherited listeners
continue
}
if isSameAddr(l.Addr(), laddr) {
n.inherited[i] = nil
n.active = append(n.active, l)
return l.(*net.UnixListener), nil
}
}
// make a fresh listener
l, err := net.ListenUnix(nett, laddr)
if err != nil {
return nil, err
}
n.active = append(n.active, l)
return l, nil
}
// activeListeners returns a snapshot copy of the active listeners.
func (n *Net) activeListeners() ([]net.Listener, error) {
n.mutex.Lock()
defer n.mutex.Unlock()
ls := make([]net.Listener, len(n.active))
copy(ls, n.active)
return ls, nil
}
func isSameAddr(a1, a2 net.Addr) bool {
if a1.Network() != a2.Network() {
return false
}
a1s := a1.String()
a2s := a2.String()
if a1s == a2s {
return true
}
// This allows for ipv6 vs ipv4 local addresses to compare as equal. This
// scenario is common when listening on localhost.
const ipv6prefix = "[::]"
a1s = strings.TrimPrefix(a1s, ipv6prefix)
a2s = strings.TrimPrefix(a2s, ipv6prefix)
const ipv4prefix = "0.0.0.0"
a1s = strings.TrimPrefix(a1s, ipv4prefix)
a2s = strings.TrimPrefix(a2s, ipv4prefix)
return a1s == a2s
}
// StartProcess starts a new process passing it the active listeners. It
// doesn't fork, but starts a new process using the same environment and
// arguments as when it was originally started. This allows for a newly
// deployed binary to be started. It returns the pid of the newly started
// process when successful.
func (n *Net) StartProcess() (int, error) {
listeners, err := n.activeListeners()
if err != nil {
return 0, err
}
// Extract the fds from the listeners.
files := make([]*os.File, len(listeners))
for i, l := range listeners {
files[i], err = l.(filer).File()
if err != nil {
return 0, err
}
defer files[i].Close()
}
// Use the original binary location. This works with symlinks such that if
// the file it points to has been changed we will use the updated symlink.
argv0, err := exec.LookPath(os.Args[0])
if err != nil {
return 0, err
}
// Pass on the environment and replace the old count key with the new one.
var env []string
for _, v := range os.Environ() {
if !strings.HasPrefix(v, envCountKeyPrefix) {
env = append(env, v)
}
}
env = append(env, fmt.Sprintf("%s%d", envCountKeyPrefix, len(listeners)))
allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
Dir: originalWD,
Env: env,
Files: allFiles,
})
if err != nil {
return 0, err
}
return process.Pid, nil
}
type filer interface {
File() (*os.File, error)
}

359
vendor/github.com/facebookgo/grace/gracenet/net_test.go generated vendored Normal file
View File

@@ -0,0 +1,359 @@
package gracenet
import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"regexp"
"syscall"
"testing"
"github.com/facebookgo/ensure"
"github.com/facebookgo/freeport"
)
func TestEmptyCountEnvVariable(t *testing.T) {
var n Net
os.Setenv(envCountKey, "")
ensure.Nil(t, n.inherit())
}
func TestZeroCountEnvVariable(t *testing.T) {
var n Net
os.Setenv(envCountKey, "0")
ensure.Nil(t, n.inherit())
}
func TestInvalidCountEnvVariable(t *testing.T) {
var n Net
os.Setenv(envCountKey, "a")
expected := regexp.MustCompile("^found invalid count value: LISTEN_FDS=a$")
ensure.Err(t, n.inherit(), expected)
}
func TestInvalidFileInherit(t *testing.T) {
var n Net
tmpfile, err := ioutil.TempFile("", "TestInvalidFileInherit-")
ensure.Nil(t, err)
defer os.Remove(tmpfile.Name())
n.fdStart = dup(t, int(tmpfile.Fd()))
os.Setenv(envCountKey, "1")
ensure.Err(t, n.inherit(), regexp.MustCompile("^error inheriting socket fd"))
ensure.DeepEqual(t, len(n.inherited), 0)
ensure.Nil(t, tmpfile.Close())
}
func TestInheritErrorOnListenTCPWithInvalidCount(t *testing.T) {
var n Net
os.Setenv(envCountKey, "a")
_, err := n.Listen("tcp", ":0")
ensure.NotNil(t, err)
}
func TestInheritErrorOnListenUnixWithInvalidCount(t *testing.T) {
var n Net
os.Setenv(envCountKey, "a")
tmpdir, err := ioutil.TempDir("", "TestInheritErrorOnListenUnixWithInvalidCount-")
ensure.Nil(t, err)
ensure.Nil(t, os.RemoveAll(tmpdir))
_, err = n.Listen("unix", filepath.Join(tmpdir, "socket"))
ensure.NotNil(t, err)
}
func TestOneTcpInherit(t *testing.T) {
var n Net
l, err := net.Listen("tcp", ":0")
ensure.Nil(t, err)
file, err := l.(*net.TCPListener).File()
ensure.Nil(t, err)
ensure.Nil(t, l.Close())
n.fdStart = dup(t, int(file.Fd()))
ensure.Nil(t, file.Close())
os.Setenv(envCountKey, "1")
ensure.Nil(t, n.inherit())
ensure.DeepEqual(t, len(n.inherited), 1)
l, err = n.Listen("tcp", l.Addr().String())
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 1)
ensure.DeepEqual(t, n.inherited[0], nil)
active, err := n.activeListeners()
ensure.Nil(t, err)
ensure.DeepEqual(t, len(active), 1)
ensure.Nil(t, l.Close())
}
func TestSecondTcpListen(t *testing.T) {
var n Net
os.Setenv(envCountKey, "")
l, err := n.Listen("tcp", ":0")
ensure.Nil(t, err)
_, err = n.Listen("tcp", l.Addr().String())
ensure.Err(t, err, regexp.MustCompile("bind: address already in use$"))
ensure.Nil(t, l.Close())
}
func TestSecondTcpListenInherited(t *testing.T) {
var n Net
l, err := net.Listen("tcp", ":0")
ensure.Nil(t, err)
file, err := l.(*net.TCPListener).File()
ensure.Nil(t, err)
ensure.Nil(t, l.Close())
n.fdStart = dup(t, int(file.Fd()))
ensure.Nil(t, file.Close())
os.Setenv(envCountKey, "1")
ensure.Nil(t, n.inherit())
ensure.DeepEqual(t, len(n.inherited), 1)
l, err = n.Listen("tcp", l.Addr().String())
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 1)
ensure.DeepEqual(t, n.inherited[0], nil)
_, err = n.Listen("tcp", l.Addr().String())
ensure.Err(t, err, regexp.MustCompile("bind: address already in use$"))
ensure.Nil(t, l.Close())
}
func TestInvalidNetwork(t *testing.T) {
var n Net
os.Setenv(envCountKey, "")
_, err := n.Listen("foo", "")
ensure.Err(t, err, regexp.MustCompile("^unknown network foo$"))
}
func TestInvalidNetworkUnix(t *testing.T) {
var n Net
os.Setenv(envCountKey, "")
_, err := n.Listen("invalid_unix_net_for_test", "")
ensure.Err(t, err, regexp.MustCompile("^unknown network invalid_unix_net_for_test$"))
}
func TestWithTcp0000(t *testing.T) {
var n Net
port, err := freeport.Get()
ensure.Nil(t, err)
addr := fmt.Sprintf("0.0.0.0:%d", port)
l, err := net.Listen("tcp", addr)
ensure.Nil(t, err)
file, err := l.(*net.TCPListener).File()
ensure.Nil(t, err)
ensure.Nil(t, l.Close())
n.fdStart = dup(t, int(file.Fd()))
ensure.Nil(t, file.Close())
os.Setenv(envCountKey, "1")
ensure.Nil(t, n.inherit())
ensure.DeepEqual(t, len(n.inherited), 1)
l, err = n.Listen("tcp", addr)
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 1)
ensure.DeepEqual(t, n.inherited[0], nil)
ensure.Nil(t, l.Close())
}
func TestWithTcpIPv6Loal(t *testing.T) {
var n Net
l, err := net.Listen("tcp", "[::]:0")
ensure.Nil(t, err)
file, err := l.(*net.TCPListener).File()
ensure.Nil(t, err)
ensure.Nil(t, l.Close())
n.fdStart = dup(t, int(file.Fd()))
ensure.Nil(t, file.Close())
os.Setenv(envCountKey, "1")
ensure.Nil(t, n.inherit())
ensure.DeepEqual(t, len(n.inherited), 1)
l, err = n.Listen("tcp", l.Addr().String())
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 1)
ensure.DeepEqual(t, n.inherited[0], nil)
ensure.Nil(t, l.Close())
}
func TestOneUnixInherit(t *testing.T) {
var n Net
tmpfile, err := ioutil.TempFile("", "TestOneUnixInherit-")
ensure.Nil(t, err)
ensure.Nil(t, tmpfile.Close())
ensure.Nil(t, os.Remove(tmpfile.Name()))
defer os.Remove(tmpfile.Name())
l, err := net.Listen("unix", tmpfile.Name())
ensure.Nil(t, err)
file, err := l.(*net.UnixListener).File()
ensure.Nil(t, err)
ensure.Nil(t, l.Close())
n.fdStart = dup(t, int(file.Fd()))
ensure.Nil(t, file.Close())
os.Setenv(envCountKey, "1")
ensure.Nil(t, n.inherit())
ensure.DeepEqual(t, len(n.inherited), 1)
l, err = n.Listen("unix", tmpfile.Name())
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 1)
ensure.DeepEqual(t, n.inherited[0], nil)
ensure.Nil(t, l.Close())
}
func TestInvalidTcpAddr(t *testing.T) {
var n Net
os.Setenv(envCountKey, "")
_, err := n.Listen("tcp", "abc")
ensure.Err(t, err, regexp.MustCompile("^missing port in address abc$"))
}
func TestTwoTCP(t *testing.T) {
var n Net
port1, err := freeport.Get()
ensure.Nil(t, err)
addr1 := fmt.Sprintf(":%d", port1)
l1, err := net.Listen("tcp", addr1)
ensure.Nil(t, err)
port2, err := freeport.Get()
ensure.Nil(t, err)
addr2 := fmt.Sprintf(":%d", port2)
l2, err := net.Listen("tcp", addr2)
ensure.Nil(t, err)
file1, err := l1.(*net.TCPListener).File()
ensure.Nil(t, err)
file2, err := l2.(*net.TCPListener).File()
ensure.Nil(t, err)
// assign both to prevent GC from kicking in the finalizer
fds := []int{dup(t, int(file1.Fd())), dup(t, int(file2.Fd()))}
n.fdStart = fds[0]
os.Setenv(envCountKey, "2")
// Close these after to ensure we get coalaced file descriptors.
ensure.Nil(t, l1.Close())
ensure.Nil(t, l2.Close())
ensure.Nil(t, n.inherit())
ensure.DeepEqual(t, len(n.inherited), 2)
l1, err = n.Listen("tcp", addr1)
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 1)
ensure.DeepEqual(t, n.inherited[0], nil)
ensure.Nil(t, l1.Close())
ensure.Nil(t, file1.Close())
l2, err = n.Listen("tcp", addr2)
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 2)
ensure.DeepEqual(t, n.inherited[1], nil)
ensure.Nil(t, l2.Close())
ensure.Nil(t, file2.Close())
}
func TestOneUnixAndOneTcpInherit(t *testing.T) {
var n Net
tmpfile, err := ioutil.TempFile("", "TestOneUnixAndOneTcpInherit-")
ensure.Nil(t, err)
ensure.Nil(t, tmpfile.Close())
ensure.Nil(t, os.Remove(tmpfile.Name()))
defer os.Remove(tmpfile.Name())
unixL, err := net.Listen("unix", tmpfile.Name())
ensure.Nil(t, err)
port, err := freeport.Get()
ensure.Nil(t, err)
addr := fmt.Sprintf(":%d", port)
tcpL, err := net.Listen("tcp", addr)
ensure.Nil(t, err)
tcpF, err := tcpL.(*net.TCPListener).File()
ensure.Nil(t, err)
unixF, err := unixL.(*net.UnixListener).File()
ensure.Nil(t, err)
// assign both to prevent GC from kicking in the finalizer
fds := []int{dup(t, int(tcpF.Fd())), dup(t, int(unixF.Fd()))}
n.fdStart = fds[0]
os.Setenv(envCountKey, "2")
// Close these after to ensure we get coalaced file descriptors.
ensure.Nil(t, tcpL.Close())
ensure.Nil(t, unixL.Close())
ensure.Nil(t, n.inherit())
ensure.DeepEqual(t, len(n.inherited), 2)
unixL, err = n.Listen("unix", tmpfile.Name())
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 1)
ensure.DeepEqual(t, n.inherited[1], nil)
ensure.Nil(t, unixL.Close())
ensure.Nil(t, unixF.Close())
tcpL, err = n.Listen("tcp", addr)
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 2)
ensure.DeepEqual(t, n.inherited[0], nil)
ensure.Nil(t, tcpL.Close())
ensure.Nil(t, tcpF.Close())
}
func TestSecondUnixListen(t *testing.T) {
var n Net
os.Setenv(envCountKey, "")
tmpfile, err := ioutil.TempFile("", "TestSecondUnixListen-")
ensure.Nil(t, err)
ensure.Nil(t, tmpfile.Close())
ensure.Nil(t, os.Remove(tmpfile.Name()))
defer os.Remove(tmpfile.Name())
l, err := n.Listen("unix", tmpfile.Name())
ensure.Nil(t, err)
_, err = n.Listen("unix", tmpfile.Name())
ensure.Err(t, err, regexp.MustCompile("bind: address already in use$"))
ensure.Nil(t, l.Close())
}
func TestSecondUnixListenInherited(t *testing.T) {
var n Net
tmpfile, err := ioutil.TempFile("", "TestSecondUnixListenInherited-")
ensure.Nil(t, err)
ensure.Nil(t, tmpfile.Close())
ensure.Nil(t, os.Remove(tmpfile.Name()))
defer os.Remove(tmpfile.Name())
l1, err := net.Listen("unix", tmpfile.Name())
ensure.Nil(t, err)
file, err := l1.(*net.UnixListener).File()
ensure.Nil(t, err)
n.fdStart = dup(t, int(file.Fd()))
ensure.Nil(t, file.Close())
os.Setenv(envCountKey, "1")
ensure.Nil(t, n.inherit())
ensure.DeepEqual(t, len(n.inherited), 1)
l2, err := n.Listen("unix", tmpfile.Name())
ensure.Nil(t, err)
ensure.DeepEqual(t, len(n.active), 1)
ensure.DeepEqual(t, n.inherited[0], nil)
_, err = n.Listen("unix", tmpfile.Name())
ensure.Err(t, err, regexp.MustCompile("bind: address already in use$"))
ensure.Nil(t, l1.Close())
ensure.Nil(t, l2.Close())
}
func TestPortZeroTwice(t *testing.T) {
var n Net
os.Setenv(envCountKey, "")
l1, err := n.Listen("tcp", ":0")
ensure.Nil(t, err)
l2, err := n.Listen("tcp", ":0")
ensure.Nil(t, err)
ensure.Nil(t, l1.Close())
ensure.Nil(t, l2.Close())
}
// We dup file descriptors because the os.Files are closed by a finalizer when
// they are GCed, which interacts badly with the fact that the OS reuses fds,
// and that we emulating inheriting the fd by it's integer value in our tests.
func dup(t *testing.T, fd int) int {
nfd, err := syscall.Dup(fd)
ensure.Nil(t, err)
return nfd
}

21
vendor/github.com/facebookgo/grace/license generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-present, Facebook, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

61
vendor/github.com/facebookgo/grace/readme.md generated vendored Normal file
View File

@@ -0,0 +1,61 @@
grace [![Build Status](https://secure.travis-ci.org/facebookgo/grace.png)](https://travis-ci.org/facebookgo/grace)
=====
Package grace provides a library that makes it easy to build socket
based servers that can be gracefully terminated & restarted (that is,
without dropping any connections).
It provides a convenient API for HTTP servers including support for TLS,
especially if you need to listen on multiple ports (for example a secondary
internal only admin server). Additionally it is implemented using the same API
as systemd providing [socket
activation](http://0pointer.de/blog/projects/socket-activation.html)
compatibility to also provide lazy activation of the server.
Usage
-----
Demo HTTP Server with graceful termination and restart:
https://github.com/facebookgo/grace/blob/master/gracedemo/demo.go
1. Install the demo application
go get github.com/facebookgo/grace/gracedemo
1. Start it in the first terminal
gracedemo
This will output something like:
2013/03/25 19:07:33 Serving [::]:48567, [::]:48568, [::]:48569 with pid 14642.
1. In a second terminal start a slow HTTP request
curl 'http://localhost:48567/sleep/?duration=20s'
1. In a third terminal trigger a graceful server restart (using the pid from your output):
kill -USR2 14642
1. Trigger another shorter request that finishes before the earlier request:
curl 'http://localhost:48567/sleep/?duration=0s'
If done quickly enough, this shows the second quick request will be served by
the new process (as indicated by the PID) while the slow first request will be
served by the first server. It shows how the active connection was gracefully
served before the server was shutdown. It is also showing that at one point
both the new as well as the old server was running at the same time.
Documentation
-------------
`http.Server` graceful termination and restart:
https://godoc.org/github.com/facebookgo/grace/gracehttp
`net.Listener` graceful termination and restart:
https://godoc.org/github.com/facebookgo/grace/gracenet