diff --git a/.gitignore b/.gitignore index 789a9f9..dfe23fd 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ config.toml public *.idx *.sqlite3 -ttarpit +./ttarpit +./tester diff --git a/.ttarpit.json b/.ttarpit.json new file mode 100644 index 0000000..acb258d --- /dev/null +++ b/.ttarpit.json @@ -0,0 +1,8 @@ +{ + "Processes": { + "tester1": { + "Command": "tester", + "Args": ["-count", "100", "-id", "1"] + } + } +} diff --git a/Makefile b/Makefile index d946797..0e1c419 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,24 @@ -build: +GO_IS_STUPID_EXE= + +ifeq '$(OS)' 'Windows_NT' + GO_IS_STUPID_EXE=.exe +endif + +build: build_tester go build . -compare: - ./ttarpit -input ./tests/images/bash_test.png -output none.png -pixel-width 4 -color-depth 16 -dither 0 - ./ttarpit -input ./tests/images/bash_test.png -output floyd.png -pixel-width 4 -color-depth 16 -dither 1 - ./ttarpit -input ./tests/images/bash_test.png -output atkinson.png -pixel-width 4 -color-depth 16 -dither 2 +run: build + ./ttarpit test: - go test MY/webapp/tests -c - ./tests.test + go test lcthw.dev/go/ttarpit/tests -c -o runtests$(GO_IS_STUPID_EXE) + ./runtests$(GO_IS_STUPID_EXE) -test_only: - go test . -c -o - ./tests.test -test.run TestSomePage +build_tester: + go build -o tester$(GO_IS_STUPID_EXE) ./tools/cmd/tester/main.go docs: - pkgsite --open + go tool pkgsite --open coverage: go build -cover -o webapp diff --git a/config/settings.go b/config/settings.go new file mode 100644 index 0000000..4a50f48 --- /dev/null +++ b/config/settings.go @@ -0,0 +1,38 @@ +package config + +import ( + "flag" + "log" + "os" + "os/exec" + "encoding/json" +) + +type Process struct { + Command string + Args []string + Ready chan string + ExecCmd *exec.Cmd +} + +type config struct { + Processes map[string]Process + ConfigPath string +} + +var Settings config + +func parseFlags(c *config) { + flag.StringVar(&c.ConfigPath, "config", ".ttarpit.json", ".ttarpit.json to load") + flag.Parse() +} + +func Load() { + parseFlags(&Settings) + + data, err := os.ReadFile(Settings.ConfigPath) + if err != nil { log.Fatal(err) } + + err = json.Unmarshal(data, &Settings) + if err != nil { log.Fatal(err) } +} diff --git a/main.go b/main.go index 9da45fa..e56cac2 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,65 @@ package main import ( "fmt" + "os/exec" + "log" + "io" + "os" + "errors" + "lcthw.dev/go/ttarpit/config" ) +func LaunchLogger(in io.Reader, out io.Writer, err error) { + if err != nil { log.Fatal(err) } + + go func() { + if _, err := io.Copy(out, in) + + err != nil { log.Printf("LOGGER: %v", err) } + }() +} + +func LaunchProcess(proc *config.Process) { + for { + proc.ExecCmd = exec.Command(proc.Command, proc.Args...) + if errors.Is(proc.ExecCmd.Err, exec.ErrDot) { + proc.ExecCmd.Err = nil + } + + fmt.Println("STARTING", proc.Command) + + stderr, err := proc.ExecCmd.StderrPipe(); + LaunchLogger(stderr, os.Stdout, err) + + stdout, err := proc.ExecCmd.StdoutPipe(); + LaunchLogger(stdout, os.Stdout, err) + + err = proc.ExecCmd.Start() + if err != nil { + log.Fatalf("FAIL %s %s err=%v", proc.Command, proc.Args, err) + } + + fmt.Println("WAITING for", proc.Command) + + fmt.Println("SENDING READY on channel") + proc.Ready<- "ready" + proc.ExecCmd.Wait() + fmt.Println("PROCESS", proc.Command, "EXITED") + } +} + func main() { - fmt.Println("Hello!") + config.Load() + + fmt.Println("CONFIG:", config.Settings.ConfigPath) + fmt.Println("COMMANDs:", config.Settings.Processes) + + for name, proc := range config.Settings.Processes { + fmt.Println("PROCESS:", name) + + proc.Ready = make(chan string) + go LaunchProcess(&proc) + } + + } diff --git a/tools/cmd/tester/main.go b/tools/cmd/tester/main.go new file mode 100644 index 0000000..b8917b7 --- /dev/null +++ b/tools/cmd/tester/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + "time" + "flag" + "sync" +) + +func Worker(name string, count int, done chan<- int, stop <-chan int) { + for i := 0; i < count; i++ { + select { + case <-stop: + fmt.Println("Worker stopped:", name) + done<- 1 + return + default: + time.Sleep(1 * time.Second) + fmt.Println("TESTER #", name) + } + } + + done<- 1 +} + +func Supervisor(id int, count int) { + stop := make(chan int) + var wg sync.WaitGroup + + for i := 0; i < 5; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + for { + workerDone := make(chan int) + go Worker(fmt.Sprintf("%d:%d", id, i), count, workerDone, stop) + + select { + case <-workerDone: + fmt.Println("Supervisor: Worker Done") + time.Sleep(500 * time.Millisecond) + case <-stop: + fmt.Println("Supervisor: Stop") + return + } + } + }() + } + + /* + go func() { + // test shutdown + time.Sleep(10000 * time.Millisecond) + close(stop) + }() + */ + + wg.Wait() +} + + +func main() { + count := flag.Int("count", 10, "Seconds to wait") + + id := flag.Int("id", 1, "Tester ID to use.") + flag.Parse() + + fmt.Println(">>> TESTER STARTS, id=", *id, "count=", *count) + + Supervisor(*id, *count) +}