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

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-----`)