diff --git a/agent/proxy/daemon.go b/agent/proxy/daemon.go index 3a8c1b11b..231f25cb8 100644 --- a/agent/proxy/daemon.go +++ b/agent/proxy/daemon.go @@ -39,8 +39,9 @@ type Daemon struct { // process is the started process lock sync.Mutex - process *os.Process + stopped bool stopCh chan struct{} + process *os.Process } // Start starts the daemon and keeps it running. @@ -50,30 +51,29 @@ func (p *Daemon) Start() error { p.lock.Lock() defer p.lock.Unlock() - // If the daemon is already started, return no error. - if p.stopCh != nil { + // A stopped proxy cannot be restarted + if p.stopped { + return fmt.Errorf("stopped") + } + + // If we're already running, that is okay + if p.process != nil { return nil } - // Start it for the first time - process, err := p.start() - if err != nil { - return err - } - - // Create the stop channel we use to notify when we've gracefully stopped + // Setup our stop channel stopCh := make(chan struct{}) p.stopCh = stopCh - // Store the process so that we can signal it later - p.process = process - + // Start the loop. go p.keepAlive(stopCh) return nil } -func (p *Daemon) keepAlive(stopCh chan struct{}) { +// keepAlive starts and keeps the configured process alive until it +// is stopped via Stop. +func (p *Daemon) keepAlive(stopCh <-chan struct{}) { p.lock.Lock() process := p.process p.lock.Unlock() @@ -106,31 +106,43 @@ func (p *Daemon) keepAlive(stopCh chan struct{}) { p.Logger.Printf( "[WARN] agent/proxy: waiting %s before restarting daemon", waitTime) - time.Sleep(waitTime) + + timer := time.NewTimer(waitTime) + select { + case <-timer.C: + // Timer is up, good! + + case <-stopCh: + // During our backoff wait, we've been signalled to + // quit, so just quit. + timer.Stop() + return + } } } p.lock.Lock() - // If we gracefully stopped (stopCh is closed) then don't restart. We - // check stopCh and not p.stopCh because the latter could reference - // a new process. - select { - case <-stopCh: + // If we gracefully stopped then don't restart. + if p.stopped { p.lock.Unlock() return - default: } - // Process isn't started currently. We're restarting. + // Process isn't started currently. We're restarting. Start it + // and save the process if we have it. var err error process, err = p.start() + if err == nil { + p.process = process + } + p.lock.Unlock() + if err != nil { p.Logger.Printf("[ERR] agent/proxy: error restarting daemon: %s", err) + continue } - p.process = process - p.lock.Unlock() } _, err := process.Wait() @@ -169,24 +181,20 @@ func (p *Daemon) Stop() error { p.lock.Lock() defer p.lock.Unlock() - // If we don't have a stopCh then we never even started yet. - if p.stopCh == nil { + // If we're already stopped or never started, then no problem. + if p.stopped || p.process == nil { + // In the case we never even started, calling Stop makes it so + // that we can't ever start in the future, either, so mark this. + p.stopped = true return nil } - // If stopCh is closed, then we're already stopped - select { - case <-p.stopCh: - return nil - default: - } + // Note that we've stopped + p.stopped = true + close(p.stopCh) err := p.process.Signal(os.Interrupt) - // This signals that we've stopped and therefore don't want to restart - close(p.stopCh) - p.stopCh = nil - return err //return p.Command.Process.Kill() } diff --git a/agent/proxy/proxy.go b/agent/proxy/proxy.go index 44228b521..a07bb5681 100644 --- a/agent/proxy/proxy.go +++ b/agent/proxy/proxy.go @@ -21,8 +21,14 @@ type Proxy interface { // proxy registration is rejected. Therefore, this should only fail if // the configuration of the proxy itself is irrecoverable, and should // retry starting for other failures. + // + // Starting an already-started proxy should not return an error. Start() error - // Stop stops the proxy. + // Stop stops the proxy and disallows it from ever being started again. + // + // If the proxy is not started yet, this should not return an error, but + // it should disallow Start from working again. If the proxy is already + // stopped, this should not return an error. Stop() error }