Release Argo Tunnel Client 2018.5.5
This commit is contained in:
parent
75d7561b5d
commit
7acd1b1fc8
|
@ -428,14 +428,11 @@ func startServer(c *cli.Context, shutdownC chan struct{}) error {
|
|||
|
||||
// update needs to be after DNS proxy is up to resolve equinox server address
|
||||
if isAutoupdateEnabled(c) {
|
||||
if initUpdate(&listeners) {
|
||||
return nil
|
||||
}
|
||||
logger.Infof("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq"))
|
||||
wg.Add(1)
|
||||
go func(){
|
||||
defer wg.Done()
|
||||
autoupdate(c.Duration("autoupdate-freq"), &listeners, shutdownC)
|
||||
errC <- autoupdate(c.Duration("autoupdate-freq"), &listeners, shutdownC)
|
||||
}()
|
||||
}
|
||||
|
||||
|
@ -451,12 +448,14 @@ func startServer(c *cli.Context, shutdownC chan struct{}) error {
|
|||
errC <- metrics.ServeMetrics(metricsListener, shutdownC, logger)
|
||||
}()
|
||||
|
||||
go notifySystemd(connectedSignal)
|
||||
if c.IsSet("pidFile") {
|
||||
go writePidFile(connectedSignal, c.String("pidfile"))
|
||||
}
|
||||
|
||||
// Serve DNS proxy stand-alone if no hostname or tag or app is going to run
|
||||
if dnsProxyStandAlone(c) {
|
||||
if c.IsSet("pidfile") {
|
||||
go writePidFile(connectedSignal, c.String("pidfile"))
|
||||
close(connectedSignal)
|
||||
}
|
||||
close(connectedSignal)
|
||||
// no grace period, handle SIGINT/SIGTERM immediately
|
||||
return waitToShutdown(&wg, errC, shutdownC, graceShutdownSignal, 0)
|
||||
}
|
||||
|
@ -481,10 +480,6 @@ func startServer(c *cli.Context, shutdownC chan struct{}) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if c.IsSet("pidFile") {
|
||||
go writePidFile(connectedSignal, c.String("pidfile"))
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
@ -521,12 +516,13 @@ func waitToShutdown(wg *sync.WaitGroup,
|
|||
return err
|
||||
}
|
||||
|
||||
func writePidFile(waitForSignal chan struct{}, pidFile string) {
|
||||
func notifySystemd(waitForSignal chan struct{}) {
|
||||
<-waitForSignal
|
||||
daemon.SdNotify(false, "READY=1")
|
||||
if pidFile == "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func writePidFile(waitForSignal chan struct{}, pidFile string) {
|
||||
<-waitForSignal
|
||||
file, err := os.Create(pidFile)
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorf("Unable to write pid to %s", pidFile)
|
||||
|
|
|
@ -48,6 +48,7 @@ func waitForSignalWithGraceShutdown(errC chan error, shutdownC, graceShutdownSig
|
|||
}
|
||||
close(shutdownC)
|
||||
case <-shutdownC:
|
||||
close(graceShutdownSignal)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -22,7 +22,7 @@ func testChannelClosed(t *testing.T, c chan struct{}) {
|
|||
case <-c:
|
||||
return
|
||||
default:
|
||||
t.Fatal("Channel should be readable")
|
||||
t.Fatal("Channel should be closed")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ func TestWaitForSignal(t *testing.T) {
|
|||
errC <- serverErr
|
||||
}()
|
||||
|
||||
// received error, shutdownC should be closed
|
||||
err := waitForSignal(errC, shutdownC)
|
||||
assert.Equal(t, serverErr, err)
|
||||
testChannelClosed(t, shutdownC)
|
||||
|
@ -72,14 +73,25 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) {
|
|||
errC <- serverErr
|
||||
}()
|
||||
|
||||
// received error, both shutdownC and graceshutdownC should be closed
|
||||
err := waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick)
|
||||
assert.Equal(t, serverErr, err)
|
||||
testChannelClosed(t, shutdownC)
|
||||
testChannelClosed(t, graceshutdownC)
|
||||
|
||||
// shutdownC closed, graceshutdownC should also be closed and no error
|
||||
errC = make(chan error)
|
||||
shutdownC = make(chan struct{})
|
||||
graceshutdownC = make(chan struct{})
|
||||
close(shutdownC)
|
||||
err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick)
|
||||
assert.NoError(t, err)
|
||||
testChannelClosed(t, shutdownC)
|
||||
testChannelClosed(t, graceshutdownC)
|
||||
|
||||
|
||||
// Test handling SIGTERM & SIGINT
|
||||
for _, sig := range []syscall.Signal{syscall.SIGTERM, syscall.SIGINT} {
|
||||
//var wg sync.WaitGroup
|
||||
errC := make(chan error)
|
||||
shutdownC = make(chan struct{})
|
||||
graceshutdownC = make(chan struct{})
|
||||
|
|
|
@ -61,29 +61,26 @@ func update(_ *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func initUpdate(listeners *gracenet.Net) bool {
|
||||
if updateApplied() {
|
||||
os.Args = append(os.Args, "--is-autoupdated=true")
|
||||
if _, err := listeners.StartProcess(); err != nil {
|
||||
logger.WithError(err).Error("Unable to restart server automatically")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func autoupdate(freq time.Duration, listeners *gracenet.Net, shutdownC chan struct{}) {
|
||||
func autoupdate(freq time.Duration, listeners *gracenet.Net, shutdownC chan struct{}) error {
|
||||
tickC := time.Tick(freq)
|
||||
for {
|
||||
if updateApplied() {
|
||||
os.Args = append(os.Args, "--is-autoupdated=true")
|
||||
if _, err := listeners.StartProcess(); err != nil {
|
||||
pid, err := listeners.StartProcess()
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Unable to restart server automatically")
|
||||
return err
|
||||
}
|
||||
close(shutdownC)
|
||||
return
|
||||
// stop old process after autoupdate. Otherwise we create a new process
|
||||
// after each update
|
||||
logger.Infof("PID of the new process is %d", pid)
|
||||
return nil
|
||||
}
|
||||
select {
|
||||
case <-tickC:
|
||||
case <-shutdownC:
|
||||
return nil
|
||||
}
|
||||
time.Sleep(freq)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,12 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
cli "gopkg.in/urfave/cli.v2"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
|
@ -19,6 +22,13 @@ import (
|
|||
const (
|
||||
windowsServiceName = "Cloudflared"
|
||||
windowsServiceDescription = "Argo Tunnel agent"
|
||||
|
||||
recoverActionDelay = time.Second * 20
|
||||
failureCountResetPeriod = time.Hour * 24
|
||||
|
||||
// not defined in golang.org/x/sys/windows package
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms681988(v=vs.85).aspx
|
||||
serviceConfigFailureActionsFlag = 4
|
||||
)
|
||||
|
||||
func runApp(app *cli.App, shutdownC chan struct{}) {
|
||||
|
@ -58,7 +68,8 @@ func runApp(app *cli.App, shutdownC chan struct{}) {
|
|||
|
||||
elog.Info(1, fmt.Sprintf("%s service starting", windowsServiceName))
|
||||
// Run executes service name by calling windowsService which is a Handler
|
||||
// interface that implements Execute method
|
||||
// interface that implements Execute method.
|
||||
// It will set service status to stop after Execute returns
|
||||
err = svc.Run(windowsServiceName, &windowsService{app: app, elog: elog, shutdownC: shutdownC})
|
||||
if err != nil {
|
||||
elog.Error(1, fmt.Sprintf("%s service failed: %v", windowsServiceName, err))
|
||||
|
@ -74,34 +85,45 @@ type windowsService struct {
|
|||
}
|
||||
|
||||
// called by the package code at the start of the service
|
||||
func (s *windowsService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
||||
func (s *windowsService) Execute(args []string, r <-chan svc.ChangeRequest, statusChan chan<- svc.Status) (ssec bool, errno uint32) {
|
||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
||||
changes <- svc.Status{State: svc.StartPending}
|
||||
go s.app.Run(args)
|
||||
statusChan <- svc.Status{State: svc.StartPending}
|
||||
errC := make(chan error)
|
||||
go func() {
|
||||
errC <- s.app.Run(args)
|
||||
}()
|
||||
statusChan <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||
|
||||
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case c := <-r:
|
||||
switch c.Cmd {
|
||||
case svc.Interrogate:
|
||||
s.elog.Info(1, fmt.Sprintf("control request 1 #%d", c))
|
||||
changes <- c.CurrentStatus
|
||||
statusChan <- c.CurrentStatus
|
||||
case svc.Stop:
|
||||
s.elog.Info(1, "received stop control request")
|
||||
break loop
|
||||
close(s.shutdownC)
|
||||
statusChan <- svc.Status{State: svc.StopPending}
|
||||
case svc.Shutdown:
|
||||
s.elog.Info(1, "received shutdown control request")
|
||||
break loop
|
||||
close(s.shutdownC)
|
||||
statusChan <- svc.Status{State: svc.StopPending}
|
||||
default:
|
||||
s.elog.Error(1, fmt.Sprintf("unexpected control request #%d", c))
|
||||
}
|
||||
case err := <-errC:
|
||||
ssec = true
|
||||
if err != nil {
|
||||
s.elog.Error(1, fmt.Sprintf("cloudflared terminated with error %v", err))
|
||||
errno = 1
|
||||
} else {
|
||||
s.elog.Info(1, "cloudflared terminated without error")
|
||||
errno = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
close(s.shutdownC)
|
||||
changes <- svc.Status{State: svc.StopPending}
|
||||
return
|
||||
}
|
||||
|
||||
func installWindowsService(c *cli.Context) error {
|
||||
|
@ -130,12 +152,18 @@ func installWindowsService(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
logger.Infof("Argo Tunnel agent service is installed")
|
||||
err = eventlog.InstallAsEventCreate(windowsServiceName, eventlog.Error|eventlog.Warning|eventlog.Info)
|
||||
if err != nil {
|
||||
s.Delete()
|
||||
logger.WithError(err).Errorf("Cannot install event logger")
|
||||
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
|
||||
}
|
||||
err = configRecoveryOption(s.Handle)
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorf("Cannot set service recovery actions")
|
||||
logger.Infof("See %s to manually configure service recovery actions", serviceUrl)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -158,6 +186,7 @@ func uninstallWindowsService(c *cli.Context) error {
|
|||
logger.Errorf("Cannot delete service %s", windowsServiceName)
|
||||
return err
|
||||
}
|
||||
logger.Infof("Argo Tunnel agent service is uninstalled")
|
||||
err = eventlog.Remove(windowsServiceName)
|
||||
if err != nil {
|
||||
logger.Errorf("Cannot remove event logger")
|
||||
|
@ -165,3 +194,58 @@ func uninstallWindowsService(c *cli.Context) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// defined in https://msdn.microsoft.com/en-us/library/windows/desktop/ms685126(v=vs.85).aspx
|
||||
type scAction int
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms685126(v=vs.85).aspx
|
||||
const (
|
||||
scActionNone scAction = iota
|
||||
scActionRestart
|
||||
scActionReboot
|
||||
scActionRunCommand
|
||||
)
|
||||
|
||||
// defined in https://msdn.microsoft.com/en-us/library/windows/desktop/ms685939(v=vs.85).aspx
|
||||
type serviceFailureActions struct {
|
||||
// time to wait to reset the failure count to zero if there are no failures in seconds
|
||||
resetPeriod uint32
|
||||
rebootMsg *uint16
|
||||
command *uint16
|
||||
// If failure count is greater than actionCount, the service controller repeats
|
||||
// the last action in actions
|
||||
actionCount uint32
|
||||
actions uintptr
|
||||
}
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms685937(v=vs.85).aspx
|
||||
// Not supported in Windows Server 2003 and Windows XP
|
||||
type serviceFailureActionsFlag struct {
|
||||
// enableActionsForStopsWithErr is of type BOOL, which is declared as
|
||||
// typedef int BOOL in C
|
||||
enableActionsForStopsWithErr int
|
||||
}
|
||||
|
||||
type recoveryAction struct {
|
||||
recoveryType uint32
|
||||
// The time to wait before performing the specified action, in milliseconds
|
||||
delay uint32
|
||||
}
|
||||
|
||||
// until https://github.com/golang/go/issues/23239 is release, we will need to
|
||||
// configure through ChangeServiceConfig2
|
||||
func configRecoveryOption(handle windows.Handle) error {
|
||||
actions := []recoveryAction{
|
||||
{recoveryType: uint32(scActionRestart), delay: uint32(recoverActionDelay / time.Millisecond)},
|
||||
}
|
||||
serviceRecoveryActions := serviceFailureActions{
|
||||
resetPeriod: uint32(failureCountResetPeriod / time.Second),
|
||||
actionCount: uint32(len(actions)),
|
||||
actions: uintptr(unsafe.Pointer(&actions[0])),
|
||||
}
|
||||
if err := windows.ChangeServiceConfig2(handle, windows.SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&serviceRecoveryActions))); err != nil {
|
||||
return err
|
||||
}
|
||||
serviceFailureActionsFlag := serviceFailureActionsFlag{enableActionsForStopsWithErr: 1}
|
||||
return windows.ChangeServiceConfig2(handle, serviceConfigFailureActionsFlag, (*byte)(unsafe.Pointer(&serviceFailureActionsFlag)))
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func (p MetricsPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dn
|
|||
status, err := plugin.NextOrFailure(p.Name(), p.Next, ctx, rw, r)
|
||||
|
||||
// Update built-in metrics
|
||||
vars.Report(state, ".", rcode.ToString(rw.Rcode), rw.Len, rw.Start)
|
||||
vars.Report(ctx, state, ".", rcode.ToString(rw.Rcode), rw.Len, rw.Start)
|
||||
|
||||
return status, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue