diff --git a/artifacts/README.md b/artifacts/README.md new file mode 100644 index 0000000..5863c36 --- /dev/null +++ b/artifacts/README.md @@ -0,0 +1,2 @@ +## Artifacts directory + diff --git a/bacillus.go b/bacillus.go index 4d142c6..7c09cac 100644 --- a/bacillus.go +++ b/bacillus.go @@ -29,10 +29,13 @@ const ( ) var ( - addrPort string - hookStd string - apiKey string - attachStdout bool + server *http.Server + addrPort string + hookStd string + apiKey string + attachStdout bool + shutdownModeActive bool + killSwitch chan bool //statUseUnicode bool indStyle string instCounter uint32 @@ -59,6 +62,9 @@ func getFavIcon() string { } func getBodyBgndHTMLFrag() string { + if shutdownModeActive { + return ` style='background: linear-gradient(to bottom, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0.8) 100%); background-image: url("/images/bacillus-shutdown.jpg"); background-size: cover;'` + } return ` style='background: linear-gradient(to bottom, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0.8) 100%); background-image: url("/images/bacillus.jpg"); background-size: cover;'` } @@ -342,6 +348,13 @@ func launchJobListener(mainCtx context.Context, jobTag, jobOpts string, jobEnv [ `) + if shutdownModeActive { + io.WriteString(w, fmt.Sprintf("
Server is in shutdown mode, come back later.
\n")) + io.WriteString(w, ` + + `) + return + } io.WriteString(w, fmt.Sprintf("
Triggered %s
\n", jobTag)) io.WriteString(w, ` @@ -669,7 +682,13 @@ func rootPageHandler(w http.ResponseWriter, r *http.Request) { .. that's about it. Happy Build Automating, DevOps-ing, or whatever it's called these days... - + + Oh, and in case you need to... + halt any new jobs for a graceful shutdown + cancel a planned shutdown + + +
Qui verifiers ratum efficiat? Non I.
`) io.WriteString(w, ` @@ -678,7 +697,61 @@ func rootPageHandler(w http.ResponseWriter, r *http.Request) { `) } +func cancelShutdownHandler(w http.ResponseWriter, r *http.Request) { + fmt.Println(r.URL) + shutdownModeActive = false + w.Header().Set("Content-type", "text/html") + io.WriteString(w, ` + + `+ + getFavIcon()+ + getGoBackHeaderJS("3000")+` + + + `) + io.WriteString(w, fmt.Sprintf("
Shutdown mode off.
\n")) + io.WriteString(w, ` + + `) +} + +func shutdownHandler(w http.ResponseWriter, r *http.Request) { + fmt.Println(r.URL) + shutdownModeActive = true + w.Header().Set("Content-type", "text/html") + io.WriteString(w, ` + + `+ + getFavIcon()+ + getGoBackHeaderJS("3000")+` + + + `) + io.WriteString(w, fmt.Sprintf("
Shutdown mode on. No new jobs can start.
\n")) + io.WriteString(w, ` + + `) +} + +func rudeShutdownHandler(w http.ResponseWriter, r *http.Request) { + fmt.Println(r.URL) + w.Header().Set("Content-type", "text/html") + io.WriteString(w, ` + + `+ + getFavIcon()+` + + + `) + io.WriteString(w, fmt.Sprintf("
.. so cold... so very, very cold..
\n")) + io.WriteString(w, ` + + `) + killSwitch <- true +} + func main() { + killSwitch = make(chan bool, 1) // ensure a single send can proceed unblocked var createRunlog bool flag.StringVar(&addrPort, "a", ":9990", "[addr]:port on which to listen") @@ -705,7 +778,7 @@ func main() { } log.SetOutput(logfile) - log.Printf("[bacillus %s startup] usage\n", appVer) + log.Printf("[bacillus %s startup]\n", appVer) log.Printf("[listening on %s, type %s]\n", addrPort, hookStd) cmdMap := make(map[string]string) @@ -783,9 +856,19 @@ func main() { // A single endpoint handles the 'live' job output http.HandleFunc("/"+jobHomeDir+"/", consoleHandler) + // Similarly, a single endpoint handles static full job output http.HandleFunc("/"+jobHomeDir+"/fullconsole/", fullConsoleHandler) + // Enter shutdown mode (stop launching new jobs) + http.HandleFunc("/shutdown", shutdownHandler) + + // Enter shutdown mode (stop launching new jobs) + http.HandleFunc("/cancelshutdown", cancelShutdownHandler) + + // Rude exit (regardless of running jobs) + http.HandleFunc("/rudeshutdown", rudeShutdownHandler) + // And finally, the root fallback to give help on defined endpoints. http.HandleFunc("/", rootPageHandler) @@ -793,5 +876,16 @@ func main() { // log.Fatal(http.ListenAndServe(":9991", http.FileServer(http.Dir(jobHomeDir)))) //}() - log.Fatal(http.ListenAndServe(addrPort, nil)) + // Rather than use http.ListenAndServe() we break out that func + // and retain the http.Server var so we can call Shutdown() or + // Close() if needed. + server = &http.Server{Addr: addrPort, Handler: nil} + go func() { + log.Fatal(server.ListenAndServe()) + }() + + // .. and wait for a rude shutdown if requested + k := <-killSwitch + fmt.Printf("Got rudeShutdown:%v\n", k) + server.Shutdown(nil) } diff --git a/images/bacillus-lg.jpg b/images/bacillus-lg.jpg new file mode 100644 index 0000000..df4d8b2 Binary files /dev/null and b/images/bacillus-lg.jpg differ diff --git a/images/bacillus-shutdown.jpg b/images/bacillus-shutdown.jpg new file mode 100644 index 0000000..555ce6d Binary files /dev/null and b/images/bacillus-shutdown.jpg differ