This commit is contained in:
Morgan Bazalgette
2017-01-14 18:42:10 +01:00
parent 41ee4c90b3
commit 3961e310b1
444 changed files with 179208 additions and 0 deletions

29
vendor/github.com/rcrowley/goagain/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,29 @@
Copyright 2012 Richard Crowley. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation
are those of the authors and should not be interpreted as representing
official policies, either expressed or implied, of Richard Crowley.

23
vendor/github.com/rcrowley/goagain/README.md generated vendored Normal file
View File

@@ -0,0 +1,23 @@
goagain
=======
Zero-downtime restarts in Go
----------------------------
The `goagain` package provides primitives for bringing zero-downtime restarts to Go applications that accept connections from a [`net.TCPListener`](http://golang.org/pkg/net/#TCPListener) or [`net.UnixListener`](http://golang.org/pkg/net/#UnixListener).
Have a look at the examples because it isn't just a matter of importing the library and everything working. Your `main` function will have to accomodate the `goagain` protocols and your process will have to have some definition (however contrived you like) of a graceful shutdown process.
Installation
------------
go get github.com/rcrowley/goagain
Usage
-----
Send `SIGUSR2` to a process using `goagain` and it will restart without downtime.
[`example/single/main.go`](https://github.com/rcrowley/goagain/blob/master/example/single/main.go): The `Single` strategy (named because it calls `execve`(2) once) operates similarly to Nginx and Unicorn. The parent forks a child, the child execs, and then the child kills the parent. This is easy to understand but doesn't play nicely with Upstart and similar direct-supervision `init`(8) daemons. It should play nicely with `systemd`.
[`example/double/main.go`](https://github.com/rcrowley/goagain/blob/master/example/double/main.go): The `Double` strategy (named because it calls `execve`(2) twice) is **experimental** so proceed with caution. The parent forks a child, the child execs, the child signals the parent, the parent execs, and finally the parent kills the child. This is regrettably much more complicated but plays nicely with Upstart and similar direct-supervision `init`(8) daemons.

284
vendor/github.com/rcrowley/goagain/goagain.go generated vendored Normal file
View File

@@ -0,0 +1,284 @@
// Zero-downtime restarts in Go.
package goagain
import (
"fmt"
"io"
"log"
"net"
"os"
"os/exec"
"os/signal"
"reflect"
"syscall"
)
type strategy int
const (
// The Single-exec strategy: parent forks child to exec with an inherited
// net.Listener; child kills parent and becomes a child of init(8).
Single strategy = iota
// The Double-exec strategy: parent forks child to exec (first) with an
// inherited net.Listener; child signals parent to exec (second); parent
// kills child.
Double
)
// Don't make the caller import syscall.
const (
SIGINT = syscall.SIGINT
SIGQUIT = syscall.SIGQUIT
SIGTERM = syscall.SIGTERM
SIGUSR2 = syscall.SIGUSR2
)
var (
// OnSIGHUP is the function called when the server receives a SIGHUP
// signal. The normal use case for SIGHUP is to reload the
// configuration.
OnSIGHUP func(l net.Listener) error
// OnSIGUSR1 is the function called when the server receives a
// SIGUSR1 signal. The normal use case for SIGUSR1 is to repon the
// log files.
OnSIGUSR1 func(l net.Listener) error
// The strategy to use; Single by default.
Strategy strategy = Single
)
// Re-exec this same image without dropping the net.Listener.
func Exec(l net.Listener) error {
var pid int
fmt.Sscan(os.Getenv("GOAGAIN_PID"), &pid)
if syscall.Getppid() == pid {
return fmt.Errorf("goagain.Exec called by a child process")
}
argv0, err := lookPath()
if nil != err {
return err
}
if _, err := setEnvs(l); nil != err {
return err
}
if err := os.Setenv(
"GOAGAIN_SIGNAL",
fmt.Sprintf("%d", syscall.SIGQUIT),
); nil != err {
return err
}
log.Println("re-executing", argv0)
return syscall.Exec(argv0, os.Args, os.Environ())
}
// Fork and exec this same image without dropping the net.Listener.
func ForkExec(l net.Listener) error {
argv0, err := lookPath()
if nil != err {
return err
}
wd, err := os.Getwd()
if nil != err {
return err
}
fd, err := setEnvs(l)
if nil != err {
return err
}
if err := os.Setenv("GOAGAIN_PID", ""); nil != err {
return err
}
if err := os.Setenv(
"GOAGAIN_PPID",
fmt.Sprint(syscall.Getpid()),
); nil != err {
return err
}
var sig syscall.Signal
if Double == Strategy {
sig = syscall.SIGUSR2
} else {
sig = syscall.SIGQUIT
}
if err := os.Setenv("GOAGAIN_SIGNAL", fmt.Sprintf("%d", sig)); nil != err {
return err
}
files := make([]*os.File, fd+1)
files[syscall.Stdin] = os.Stdin
files[syscall.Stdout] = os.Stdout
files[syscall.Stderr] = os.Stderr
addr := l.Addr()
files[fd] = os.NewFile(
fd,
fmt.Sprintf("%s:%s->", addr.Network(), addr.String()),
)
p, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
Dir: wd,
Env: os.Environ(),
Files: files,
Sys: &syscall.SysProcAttr{},
})
if nil != err {
return err
}
log.Println("spawned child", p.Pid)
if err = os.Setenv("GOAGAIN_PID", fmt.Sprint(p.Pid)); nil != err {
return err
}
return nil
}
// Test whether an error is equivalent to net.errClosing as returned by
// Accept during a graceful exit.
func IsErrClosing(err error) bool {
if opErr, ok := err.(*net.OpError); ok {
err = opErr.Err
}
return "use of closed network connection" == err.Error()
}
// Kill process specified in the environment with the signal specified in the
// environment; default to SIGQUIT.
func Kill() error {
var (
pid int
sig syscall.Signal
)
_, err := fmt.Sscan(os.Getenv("GOAGAIN_PID"), &pid)
if io.EOF == err {
_, err = fmt.Sscan(os.Getenv("GOAGAIN_PPID"), &pid)
}
if nil != err {
return err
}
if _, err := fmt.Sscan(os.Getenv("GOAGAIN_SIGNAL"), &sig); nil != err {
sig = syscall.SIGQUIT
}
if syscall.SIGQUIT == sig && Double == Strategy {
go syscall.Wait4(pid, nil, 0, nil)
}
log.Println("sending signal", sig, "to process", pid)
return syscall.Kill(pid, sig)
}
// Reconstruct a net.Listener from a file descriptior and name specified in the
// environment. Deal with Go's insistence on dup(2)ing file descriptors.
func Listener() (l net.Listener, err error) {
var fd uintptr
if _, err = fmt.Sscan(os.Getenv("GOAGAIN_FD"), &fd); nil != err {
return
}
l, err = net.FileListener(os.NewFile(fd, os.Getenv("GOAGAIN_NAME")))
if nil != err {
return
}
switch l.(type) {
case *net.TCPListener, *net.UnixListener:
default:
err = fmt.Errorf(
"file descriptor is %T not *net.TCPListener or *net.UnixListener",
l,
)
return
}
if err = syscall.Close(int(fd)); nil != err {
return
}
return
}
// Block this goroutine awaiting signals. Signals are handled as they
// are by Nginx and Unicorn: <http://unicorn.bogomips.org/SIGNALS.html>.
func Wait(l net.Listener) (syscall.Signal, error) {
ch := make(chan os.Signal, 2)
signal.Notify(
ch,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGQUIT,
syscall.SIGTERM,
syscall.SIGUSR1,
syscall.SIGUSR2,
)
forked := false
for {
sig := <-ch
log.Println(sig.String())
switch sig {
// SIGHUP should reload configuration.
case syscall.SIGHUP:
if nil != OnSIGHUP {
if err := OnSIGHUP(l); nil != err {
log.Println("OnSIGHUP:", err)
}
}
// SIGINT should exit.
case syscall.SIGINT:
return syscall.SIGINT, nil
// SIGQUIT should exit gracefully.
case syscall.SIGQUIT:
return syscall.SIGQUIT, nil
// SIGTERM should exit.
case syscall.SIGTERM:
return syscall.SIGTERM, nil
// SIGUSR1 should reopen logs.
case syscall.SIGUSR1:
if nil != OnSIGUSR1 {
if err := OnSIGUSR1(l); nil != err {
log.Println("OnSIGUSR1:", err)
}
}
// SIGUSR2 forks and re-execs the first time it is received and execs
// without forking from then on.
case syscall.SIGUSR2:
if forked {
return syscall.SIGUSR2, nil
}
forked = true
if err := ForkExec(l); nil != err {
return syscall.SIGUSR2, err
}
}
}
}
func lookPath() (argv0 string, err error) {
argv0, err = exec.LookPath(os.Args[0])
if nil != err {
return
}
if _, err = os.Stat(argv0); nil != err {
return
}
return
}
func setEnvs(l net.Listener) (fd uintptr, err error) {
v := reflect.ValueOf(l).Elem().FieldByName("fd").Elem()
fd = uintptr(v.FieldByName("sysfd").Int())
_, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0)
if 0 != e1 {
err = e1
return
}
if err = os.Setenv("GOAGAIN_FD", fmt.Sprint(fd)); nil != err {
return
}
addr := l.Addr()
if err = os.Setenv(
"GOAGAIN_NAME",
fmt.Sprintf("%s:%s->", addr.Network(), addr.String()),
); nil != err {
return
}
return
}

32
vendor/github.com/rcrowley/goagain/legacy.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package goagain
import (
"fmt"
"net"
"os"
"syscall"
)
// Block this goroutine awaiting signals. Signals are handled as they
// are by Nginx and Unicorn: <http://unicorn.bogomips.org/SIGNALS.html>.
func AwaitSignals(l net.Listener) (err error) {
_, err = Wait(l)
return
}
// Convert and validate the GOAGAIN_FD, GOAGAIN_NAME, and GOAGAIN_PPID
// environment variables. If all three are present and in order, this
// is a child process that may pick up where the parent left off.
func GetEnvs() (l net.Listener, ppid int, err error) {
if _, err = fmt.Sscan(os.Getenv("GOAGAIN_PPID"), &ppid); nil != err {
return
}
l, err = Listener()
return
}
// Send SIGQUIT to the given ppid in order to complete the handoff to the
// child process.
func KillParent(ppid int) error {
return syscall.Kill(ppid, syscall.SIGQUIT)
}

54
vendor/github.com/rcrowley/goagain/test.sh generated vendored Normal file
View File

@@ -0,0 +1,54 @@
set -e -x
cd "$(dirname "$0")"
findproc() {
set +x
find "/proc" -mindepth 2 -maxdepth 2 -name "exe" -lname "$PWD/$1" 2>"/dev/null" |
cut -d"/" -f"3"
set -x
}
for NAME in "legacy" "single"
do
cd "example/$NAME"
go build
./$NAME &
PID="$!"
[ "$PID" -a -d "/proc/$PID" ]
for _ in _ _
do
OLDPID="$PID"
sleep 1
kill -USR2 "$PID"
sleep 2
PID="$(findproc "$NAME")"
[ ! -d "/proc/$OLDPID" -a "$PID" -a -d "/proc/$PID" ]
done
[ "$(nc "127.0.0.1" "48879")" = "Hello, world!" ]
kill -TERM "$PID"
sleep 2
[ ! -d "/proc/$PID" ]
[ -z "$(findproc "$NAME")" ]
cd "$OLDPWD"
done
cd "example/double"
go build
./double &
PID="$!"
[ "$PID" -a -d "/proc/$PID" ]
for _ in _ _
do
sleep 1
kill -USR2 "$PID"
sleep 3
NEWPID="$(findproc "double")"
[ "$NEWPID" = "$PID" -a -d "/proc/$PID" ]
done
[ "$(nc "127.0.0.1" "48879")" = "Hello, world!" ]
kill -TERM "$PID"
sleep 3
[ ! -d "/proc/$PID" ]
[ -z "$(findproc "double")" ]
cd "$OLDPWD"