From 0f7e13222d087d85c1afd67d22d36f544a39801d Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Sat, 28 Sep 2024 09:45:03 +0200 Subject: feat(tfstate): bootstrap an http server that answers /healthz --- .gitignore | 1 + cmd/tfstate/healthz.go | 12 +++++++ cmd/tfstate/main.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/tfstate/routes.go | 7 ++++ go.mod | 3 ++ 5 files changed, 116 insertions(+) create mode 100644 .gitignore create mode 100644 cmd/tfstate/healthz.go create mode 100644 cmd/tfstate/main.go create mode 100644 cmd/tfstate/routes.go create mode 100644 go.mod diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1788154 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tfstate diff --git a/cmd/tfstate/healthz.go b/cmd/tfstate/healthz.go new file mode 100644 index 0000000..20c72c9 --- /dev/null +++ b/cmd/tfstate/healthz.go @@ -0,0 +1,12 @@ +package main + +import "net/http" + +func handleHealthz() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Cache-Control", "no-store, no-cache") + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("{}")) + }) +} diff --git a/cmd/tfstate/main.go b/cmd/tfstate/main.go new file mode 100644 index 0000000..e0b33a5 --- /dev/null +++ b/cmd/tfstate/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "context" + "fmt" + "io" + "log" + "log/slog" + "net" + "net/http" + "os" + "os/signal" + "sync" + "time" +) + +type Config struct { + Host string + Port string +} + +func run( + ctx context.Context, + config *Config, + args []string, + getenv func(string) string, + stdin io.Reader, + stdout, stderr io.Writer, +) error { + ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) + defer cancel() + + mux := http.NewServeMux() + addRoutes( + mux, + ) + httpServer := &http.Server{ + Addr: net.JoinHostPort(config.Host, config.Port), + Handler: mux, + } + go func() { + log.Printf("listening on %s\n", httpServer.Addr) + if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + fmt.Fprintf(os.Stderr, "error listening and serving: %+v\n", err) + } + }() + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + <-ctx.Done() + shutdownCtx := context.Background() + shutdownCtx, cancel := context.WithTimeout(shutdownCtx, 10*time.Second) + defer cancel() + if err := httpServer.Shutdown(shutdownCtx); err != nil { + fmt.Fprintf(os.Stderr, "error shutting down http server: %+v\n", err) + } + }() + wg.Wait() + + return nil +} + +func main() { + ctx := context.Background() + + var opts *slog.HandlerOptions + if os.Getenv("TFSTATE_DEBUG") != "" { + opts = &slog.HandlerOptions{ + AddSource: true, + Level: slog.LevelDebug, + } + } + logger := slog.New(slog.NewJSONHandler(os.Stdout, opts)) + slog.SetDefault(logger) + + config := Config{ + Host: "0.0.0.0", + Port: "8080", + } + + if err := run( + ctx, + &config, + os.Args, + os.Getenv, + os.Stdin, + os.Stdout, os.Stderr, + ); err != nil { + fmt.Fprintf(os.Stderr, "%+v\n", err) + os.Exit(1) + } +} diff --git a/cmd/tfstate/routes.go b/cmd/tfstate/routes.go new file mode 100644 index 0000000..f583fc7 --- /dev/null +++ b/cmd/tfstate/routes.go @@ -0,0 +1,7 @@ +package main + +import "net/http" + +func addRoutes(mux *http.ServeMux) { + mux.Handle("GET /healthz", handleHealthz()) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0fa79e2 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.adyxax.org/adyxax/tfstate + +go 1.23.1 -- cgit v1.2.3