AppRunner: Shutdown on SIGTERM and SIGINT signals
Implement /quitquitquit handler
Change-Id: I0d11791bf3b2831b71548e2ca573e34ad854c561
diff --git a/apps/app-runner/main.go b/apps/app-runner/main.go
index 0449ca5..b11a977 100644
--- a/apps/app-runner/main.go
+++ b/apps/app-runner/main.go
@@ -7,8 +7,10 @@
"log"
"net"
"os"
+ "os/signal"
"path/filepath"
"strings"
+ "syscall"
"golang.org/x/crypto/ssh"
@@ -129,7 +131,16 @@
panic(err)
}
s := NewServer(*agentMode, *port, *appId, *service, id, *repoAddr, *branch, *rootDir, signer, *appDir, cmds, self, *managerAddr)
- if err := s.Start(); err != nil {
- log.Fatal(err)
- }
+ go func() {
+ if err := s.Start(); err != nil {
+ log.Fatal(err)
+ } else {
+ log.Println("Done")
+ os.Exit(0)
+ }
+ }()
+ sigChan := make(chan os.Signal, 1)
+ signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+ <-sigChan
+ s.Stop()
}
diff --git a/apps/app-runner/server.go b/apps/app-runner/server.go
index 1f44481..51e3db9 100644
--- a/apps/app-runner/server.go
+++ b/apps/app-runner/server.go
@@ -2,7 +2,9 @@
import (
"bytes"
+ "context"
"encoding/json"
+ "errors"
"fmt"
"io"
"net/http"
@@ -30,7 +32,8 @@
}
type Server struct {
- l sync.Locker
+ l sync.Locker
+ hs *http.Server
// TODO(gio): randomly generate string
runId int
agentMode bool
@@ -52,6 +55,7 @@
logM io.Writer
currDir string
status *Status
+ stop bool
}
func NewServer(agentMode bool, port int, appId, service, id, repoAddr, branch, rootDir string, signer ssh.Signer, appDir string, runCommands []Command, self string, manager string) *Server {
@@ -59,6 +63,7 @@
logM := io.MultiWriter(os.Stdout, logger)
return &Server{
l: &sync.Mutex{},
+ hs: nil,
runId: 0,
agentMode: agentMode,
port: port,
@@ -78,6 +83,7 @@
logM: logM,
currDir: "",
status: nil,
+ stop: false,
}
}
@@ -85,13 +91,45 @@
http.HandleFunc("/update", s.handleUpdate)
http.HandleFunc("/ready", s.handleReady)
http.HandleFunc("/logs", s.handleLogs)
+ http.HandleFunc("/quitquitquit", s.handleQuit)
if s.managerAddr != "" && s.appId != "" {
go s.pingManager()
}
if err := s.run(); err != nil {
return err
}
- return http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil)
+ hs := &http.Server{
+ Addr: fmt.Sprintf(":%d", s.port),
+ }
+ s.hs = hs
+ if err := s.hs.ListenAndServe(); err == nil || errors.Is(err, http.ErrServerClosed) {
+ return nil
+ } else {
+ return err
+ }
+}
+
+func (s *Server) Stop() {
+ fmt.Println("Stopping")
+ s.l.Lock()
+ defer s.l.Unlock()
+ s.stop = true
+ if err := s.kill(); err != nil {
+ fmt.Printf("Failed to stop last command: %s\n", err)
+ } else {
+ fmt.Println("Stopped last command")
+ }
+ if s.hs != nil {
+ if err := s.hs.Shutdown(context.Background()); err != nil {
+ fmt.Printf("Failed to stop web server: %s\n", err)
+ } else {
+ fmt.Println("Stopped web server")
+ }
+ }
+}
+
+func (s *Server) handleQuit(w http.ResponseWriter, r *http.Request) {
+ go s.Stop()
}
func (s *Server) handleLogs(w http.ResponseWriter, r *http.Request) {
@@ -286,8 +324,13 @@
func (s *Server) pingManager() {
defer func() {
go func() {
- time.Sleep(500 * time.Millisecond)
- s.pingManager()
+ s.l.Lock()
+ defer s.l.Unlock()
+ // TODO(gio): Wait until all logs are sent over to the manager.
+ if !s.stop {
+ time.Sleep(500 * time.Millisecond)
+ s.pingManager()
+ }
}()
}()
logItems := s.logs.Items()