Skip to content

Instantly share code, notes, and snippets.

@yougg
Last active April 25, 2021 02:55
Show Gist options
  • Save yougg/fb2abf3fdfc085209f7004aca9847593 to your computer and use it in GitHub Desktop.
Save yougg/fb2abf3fdfc085209f7004aca9847593 to your computer and use it in GitHub Desktop.
Simple monitor for running multi process in docker scratch image
//go:generate go build -trimpath -buildmode pie -installsuffix netgo -tags "osusergo netgo static_build" -ldflags "-s -w -extldflags '-static'" -o monitor ${GOFILE}
package main
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
)
var (
command = flag.String("c", "", "")
script = flag.String("s", "", "")
debug = flag.Bool("d", false, "")
interval = flag.Duration("i", 10*time.Second, "")
count = flag.Uint("r", 0, "")
)
var (
regSpace = regexp.MustCompile(`[\s]+`)
wg = &sync.WaitGroup{}
)
func init() {
flag.StringVar(command, "command", "", "command line file")
flag.StringVar(script, "script", "", "script file")
flag.BoolVar(debug, "debug", false, "debug mode")
flag.DurationVar(interval, "interval", time.Minute, "interval time for restart failure program")
flag.UintVar(count, "retry", 0, "retry times on error, -1 for ever")
flag.Usage = func() {
name, _ := os.Executable()
name = filepath.Base(name)
usage := `Usage of ` + name + `:` +
"\n -c, --command\n\tcommand line(s) to be execute (default \"\")" +
"\n -s, --script\n\tread script file and run commands (default \"\")" +
"\n -i, --interval\n\tinterval time(ns/us/ms/s/m/h) for restart failure program (default \"10s\")" +
"\n -r, --retry\n\tretry times on error (default 0)" +
"\n -d, --debug\n\tdebug mode, attach to program's stdin/stdout/stderr" +
"\nExample:" +
"\n monitor -d -c 'go version'" +
"\n monitor -c 'date\nping -c 4 127.0.0.1\nsleep 5s'" +
"\n monitor -s xxx.script -i 100s"
fmt.Println(usage)
}
flag.Parse()
}
func main() {
var err error
data := bytes.TrimSpace([]byte(*command))
if len(data) == 0 {
data, err = ioutil.ReadFile(*script)
if err != nil {
fmt.Println("Read script file failed", err)
return
}
}
content := bytes.Split(data, []byte{'\n'})
for _, s := range content {
line := string(bytes.TrimSpace(s))
if len(line) == 0 {
continue
}
if strings.HasPrefix(line, `#`) || strings.HasPrefix(line, `//`) {
fmt.Println("Skip:", line)
continue
}
cmd := regSpace.Split(line, -1) // TODO parse 1. spaces in parameter, 2. quoted spaces in parameter
fmt.Println("Run: ", cmd)
if strings.ToLower(cmd[0]) == `sleep` {
d := time.Second
if len(cmd) > 1 {
d, err = time.ParseDuration(cmd[1])
if err != nil || d == 0 {
d = time.Second
}
}
time.Sleep(d)
continue
}
wg.Add(1)
go run(cmd...)
}
// select {}
wg.Wait()
}
func run(args ...string) {
defer wg.Done()
if len(args) == 0 {
return
}
cmd := exec.Command(args[0], args[1:]...)
if cmd == nil {
fmt.Println("Construct running command failed", args)
return
}
if *debug {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
err := cmd.Run()
if err != nil {
fmt.Println("Error:", err)
if err == exec.ErrNotFound {
return
}
}
for i := uint(0); (err != nil && i < *count) || *count < 0; i++ {
time.Sleep(*interval)
err = cmd.Run()
if err != nil {
fmt.Println("Error:", err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment