TUN-2872: Exit with non-0 status code when the binary is updated so launchd will restart the service

This commit is contained in:
cthuang 2020-04-28 08:55:27 +08:00
parent 2cf327ba01
commit c3fa4552aa
7 changed files with 75 additions and 21 deletions

View File

@ -118,10 +118,6 @@ case "$1" in
echo "Starting $name" echo "Starting $name"
$cmd >> "$stdout_log" 2>> "$stderr_log" & $cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file" echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi fi
;; ;;
stop) stop)

View File

@ -60,7 +60,7 @@ func newLaunchdTemplate(installPath, stdoutPath, stderrPath string) *ServiceTemp
<false/> <false/>
</dict> </dict>
<key>ThrottleInterval</key> <key>ThrottleInterval</key>
<integer>20</integer> <integer>5</integer>
</dict> </dict>
</plist>`, launchdIdentifier, stdoutPath, stderrPath), </plist>`, launchdIdentifier, stdoutPath, stderrPath),
} }

View File

@ -39,6 +39,7 @@ import (
"github.com/getsentry/raven-go" "github.com/getsentry/raven-go"
"github.com/gliderlabs/ssh" "github.com/gliderlabs/ssh"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/mitchellh/go-homedir"
"github.com/pkg/errors" "github.com/pkg/errors"
"gopkg.in/urfave/cli.v2" "gopkg.in/urfave/cli.v2"
"gopkg.in/urfave/cli.v2/altsrc" "gopkg.in/urfave/cli.v2/altsrc"
@ -433,7 +434,6 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
defer wg.Done() defer wg.Done()
errC <- origin.StartTunnelDaemon(ctx, tunnelConfig, connectedSignal, cloudflaredID, reconnectCh) errC <- origin.StartTunnelDaemon(ctx, tunnelConfig, connectedSignal, cloudflaredID, reconnectCh)
}() }()
return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, c.Duration("grace-period")) return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, c.Duration("grace-period"))
} }
@ -607,9 +607,15 @@ func notifySystemd(waitForSignal *signal.Signal) {
func writePidFile(waitForSignal *signal.Signal, pidFile string) { func writePidFile(waitForSignal *signal.Signal, pidFile string) {
<-waitForSignal.Wait() <-waitForSignal.Wait()
file, err := os.Create(pidFile) expandedPath, err := homedir.Expand(pidFile)
if err != nil { if err != nil {
logger.WithError(err).Errorf("Unable to write pid to %s", pidFile) logger.WithError(err).Errorf("Unable to expand %s, try to use absolute path in --pidfile", pidFile)
return
}
file, err := os.Create(expandedPath)
if err != nil {
logger.WithError(err).Errorf("Unable to write pid to %s", expandedPath)
return
} }
defer file.Close() defer file.Close()
fmt.Fprintf(file, "%d", os.Getpid()) fmt.Fprintf(file, "%d", os.Getpid())

View File

@ -17,9 +17,11 @@ func waitForSignal(errC chan error, shutdownC chan struct{}) error {
select { select {
case err := <-errC: case err := <-errC:
logger.Infof("terminating due to error: %v", err)
close(shutdownC) close(shutdownC)
return err return err
case <-signals: case s := <-signals:
logger.Infof("terminating due to signal %s", s)
close(shutdownC) close(shutdownC)
case <-shutdownC: case <-shutdownC:
} }
@ -44,10 +46,12 @@ func waitForSignalWithGraceShutdown(errC chan error,
select { select {
case err := <-errC: case err := <-errC:
logger.Infof("Initiating graceful shutdown due to %v ...", err)
close(graceShutdownC) close(graceShutdownC)
close(shutdownC) close(shutdownC)
return err return err
case <-signals: case s := <-signals:
logger.Infof("Initiating graceful shutdown due to signal %s ...", s)
close(graceShutdownC) close(graceShutdownC)
waitForGracePeriod(signals, errC, shutdownC, gracePeriod) waitForGracePeriod(signals, errC, shutdownC, gracePeriod)
case <-graceShutdownC: case <-graceShutdownC:
@ -64,7 +68,6 @@ func waitForGracePeriod(signals chan os.Signal,
shutdownC chan struct{}, shutdownC chan struct{},
gracePeriod time.Duration, gracePeriod time.Duration,
) { ) {
logger.Infof("Initiating graceful shutdown...")
// Unregister signal handler early, so the client can send a second SIGTERM/SIGINT // Unregister signal handler early, so the client can send a second SIGTERM/SIGINT
// to force shutdown cloudflared // to force shutdown cloudflared
signal.Stop(signals) signal.Stop(signals)

View File

@ -2,6 +2,7 @@ package updater
import ( import (
"context" "context"
"fmt"
"os" "os"
"runtime" "runtime"
"time" "time"
@ -32,6 +33,33 @@ EKx0BZogHSor9Wy5VztdFaAaVbsJiCbO
logger = log.CreateLogger() logger = log.CreateLogger()
) )
// BinaryUpdated implements ExitCoder interface, the app will exit with status code 11
// https://pkg.go.dev/gopkg.in/urfave/cli.v2?tab=doc#ExitCoder
type statusSuccess struct {
newVersion string
}
func (u *statusSuccess) Error() string {
return fmt.Sprintf("cloudflared has been updated to version %s", u.newVersion)
}
func (u *statusSuccess) ExitCode() int {
return 11
}
// UpdateErr implements ExitCoder interface, the app will exit with status code 10
type statusErr struct {
err error
}
func (e *statusErr) Error() string {
return fmt.Sprintf("failed to update cloudflared: %v", e.err)
}
func (e *statusErr) ExitCode() int {
return 10
}
type UpdateOutcome struct { type UpdateOutcome struct {
Updated bool Updated bool
Version string Version string
@ -67,14 +95,15 @@ func checkForUpdateAndApply() UpdateOutcome {
func Update(_ *cli.Context) error { func Update(_ *cli.Context) error {
updateOutcome := loggedUpdate() updateOutcome := loggedUpdate()
if updateOutcome.Error != nil { if updateOutcome.Error != nil {
os.Exit(10) return &statusErr{updateOutcome.Error}
} }
if updateOutcome.noUpdate() { if updateOutcome.noUpdate() {
logger.Infof("cloudflared is up to date (%s)", updateOutcome.Version) logger.Infof("cloudflared is up to date (%s)", updateOutcome.Version)
return nil
} }
return updateOutcome.Error return &statusSuccess{newVersion: updateOutcome.Version}
} }
// Checks for an update and applies it if one is available // Checks for an update and applies it if one is available
@ -126,15 +155,19 @@ func (a *AutoUpdater) Run(ctx context.Context) error {
updateOutcome := loggedUpdate() updateOutcome := loggedUpdate()
if updateOutcome.Updated { if updateOutcome.Updated {
os.Args = append(os.Args, "--is-autoupdated=true") os.Args = append(os.Args, "--is-autoupdated=true")
pid, err := a.listeners.StartProcess() if IsSysV() {
if err != nil { // SysV doesn't have a mechanism to keep service alive, we have to restart the process
logger.WithError(err).Error("Unable to restart server automatically") logger.Infof("Restarting service managed by SysV...")
return err pid, err := a.listeners.StartProcess()
if err != nil {
logger.WithError(err).Error("Unable to restart server automatically")
return &statusErr{err: err}
}
// stop old process after autoupdate. Otherwise we create a new process
// after each update
logger.Infof("PID of the new process is %d", pid)
} }
// stop old process after autoupdate. Otherwise we create a new process return &statusSuccess{newVersion: updateOutcome.Version}
// after each update
logger.Infof("PID of the new process is %d", pid)
return nil
} }
} }
select { select {
@ -187,3 +220,14 @@ func SupportAutoUpdate() bool {
func isRunningFromTerminal() bool { func isRunningFromTerminal() bool {
return terminal.IsTerminal(int(os.Stdout.Fd())) return terminal.IsTerminal(int(os.Stdout.Fd()))
} }
func IsSysV() bool {
if runtime.GOOS != "linux" {
return false
}
if _, err := os.Stat("/run/systemd/system"); err == nil {
return false
}
return true
}

1
go.mod
View File

@ -3,6 +3,7 @@ module github.com/cloudflare/cloudflared
go 1.12 go 1.12
require ( require (
github.com/BurntSushi/go-sumtype v0.0.0-20190304192233-fcb4a6205bdc // indirect
github.com/DATA-DOG/go-sqlmock v1.3.3 github.com/DATA-DOG/go-sqlmock v1.3.3
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect
github.com/aws/aws-sdk-go v1.25.8 github.com/aws/aws-sdk-go v1.25.8

4
go.sum
View File

@ -1,6 +1,8 @@
bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4= bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/go-sumtype v0.0.0-20190304192233-fcb4a6205bdc h1:nvTP+jmloR0+J4YQur/rLRdLcGVEU4SquDgH+Bo7gBY=
github.com/BurntSushi/go-sumtype v0.0.0-20190304192233-fcb4a6205bdc/go.mod h1:7yTWMMG2vOm4ABVciEt4EgNVP7fxwtcKIb/EuiLiKqY=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08= github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
@ -244,9 +246,11 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=