mirror of https://github.com/maxcnunes/gaper.git
Add support to restart on program exit
This commit is contained in:
parent
99cc752b38
commit
68c9d713ae
33
main.go
33
main.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -167,24 +168,40 @@ func runGaper(cfg *Config) error {
|
||||||
return fmt.Errorf("watcher error: %v", err)
|
return fmt.Errorf("watcher error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var changeRestart bool
|
||||||
|
|
||||||
go watcher.Watch()
|
go watcher.Watch()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-watcher.Events:
|
case event := <-watcher.Events:
|
||||||
logger.Debug("Detected new changed file: ", event)
|
logger.Debug("Detected new changed file: ", event)
|
||||||
|
changeRestart = true
|
||||||
restart(builder, runner)
|
restart(builder, runner)
|
||||||
case err := <-watcher.Errors:
|
case err := <-watcher.Errors:
|
||||||
return fmt.Errorf("error on watching files: %v", err)
|
return fmt.Errorf("error on watching files: %v", err)
|
||||||
|
case err := <-runner.Errors():
|
||||||
|
if changeRestart {
|
||||||
|
changeRestart = false
|
||||||
|
} else {
|
||||||
|
logger.Debug("Detected program exit: ", err)
|
||||||
|
if err = handleProgramExit(builder, runner, err); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
logger.Debug("Waiting watch event")
|
|
||||||
time.Sleep(time.Duration(cfg.PollInterval) * time.Millisecond)
|
time.Sleep(time.Duration(cfg.PollInterval) * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restart(builder Builder, runner Runner) error {
|
func restart(builder Builder, runner Runner) error {
|
||||||
if err := runner.Kill(); err != nil {
|
logger.Debug("Restarting program")
|
||||||
return fmt.Errorf("kill error: %v", err)
|
|
||||||
|
// kill process if it is running
|
||||||
|
if !runner.Exited() {
|
||||||
|
if err := runner.Kill(); err != nil {
|
||||||
|
return fmt.Errorf("kill error: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := builder.Build(); err != nil {
|
if err := builder.Build(); err != nil {
|
||||||
|
@ -198,6 +215,16 @@ func restart(builder Builder, runner Runner) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleProgramExit(builder Builder, runner Runner, err error) error {
|
||||||
|
_, ok := err.(*exec.ExitError)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("couldn't handle program crash restart: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
restart(builder, runner)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func shutdown(runner Runner) {
|
func shutdown(runner Runner) {
|
||||||
c := make(chan os.Signal, 2)
|
c := make(chan os.Signal, 2)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
17
runner.go
17
runner.go
|
@ -16,6 +16,8 @@ const OSWindows = "windows"
|
||||||
type Runner interface {
|
type Runner interface {
|
||||||
Run() (*exec.Cmd, error)
|
Run() (*exec.Cmd, error)
|
||||||
Kill() error
|
Kill() error
|
||||||
|
Errors() chan error
|
||||||
|
Exited() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type runner struct {
|
type runner struct {
|
||||||
|
@ -24,6 +26,7 @@ type runner struct {
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
command *exec.Cmd
|
command *exec.Cmd
|
||||||
starttime time.Time
|
starttime time.Time
|
||||||
|
errors chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRunner ...
|
// NewRunner ...
|
||||||
|
@ -33,6 +36,7 @@ func NewRunner(writer io.Writer, bin string, args []string) Runner {
|
||||||
args: args,
|
args: args,
|
||||||
writer: writer,
|
writer: writer,
|
||||||
starttime: time.Now(),
|
starttime: time.Now(),
|
||||||
|
errors: make(chan error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +95,11 @@ func (r *runner) Exited() bool {
|
||||||
return r.command != nil && r.command.ProcessState != nil && r.command.ProcessState.Exited()
|
return r.command != nil && r.command.ProcessState != nil && r.command.ProcessState.Exited()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Errors ...
|
||||||
|
func (r *runner) Errors() chan error {
|
||||||
|
return r.errors
|
||||||
|
}
|
||||||
|
|
||||||
func (r *runner) runBin() error {
|
func (r *runner) runBin() error {
|
||||||
r.command = exec.Command(r.bin, r.args...) // nolint gas
|
r.command = exec.Command(r.bin, r.args...) // nolint gas
|
||||||
stdout, err := r.command.StdoutPipe()
|
stdout, err := r.command.StdoutPipe()
|
||||||
|
@ -113,7 +122,13 @@ func (r *runner) runBin() error {
|
||||||
// TODO: handle or log errors
|
// TODO: handle or log errors
|
||||||
go io.Copy(r.writer, stdout) // nolint errcheck
|
go io.Copy(r.writer, stdout) // nolint errcheck
|
||||||
go io.Copy(r.writer, stderr) // nolint errcheck
|
go io.Copy(r.writer, stderr) // nolint errcheck
|
||||||
go r.command.Wait() // nolint errcheck
|
|
||||||
|
// wait for exit errors
|
||||||
|
go func() {
|
||||||
|
if err := r.command.Wait(); err != nil {
|
||||||
|
r.errors <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) // nolint gas
|
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) // nolint gas
|
||||||
})
|
})
|
||||||
|
|
||||||
|
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Fatal("Forced failure")
|
||||||
|
})
|
||||||
|
|
||||||
log.Println("Starting server")
|
log.Println("Starting server")
|
||||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue