Compare commits
1
Commits
next
...
d209d87b3f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d209d87b3f |
@@ -23,6 +23,14 @@ latest run passes.
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-10-03: the backend's logs are one stream (issue #27): fx logs its own
|
||||||
|
steps of starting and stopping through the backend's logger, so off a terminal
|
||||||
|
every line from start to stop is JSON, where fx used to write plain text to
|
||||||
|
stderr. The backend logs its name, version and architecture once at start. The
|
||||||
|
health check's uptime keys are now `uptime_seconds` and `uptime_human`; its
|
||||||
|
path, content type, `"status":"ok"` and 200 are unchanged. `SENTRY_DSN`,
|
||||||
|
`METRICS_USERNAME` and `METRICS_PASSWORD` are still read and still unused, and
|
||||||
|
left out of `backend/README.md`, until issues #94 and #95 wire them up
|
||||||
- 2026-10-03: root no longer acts outside `/data` when it prepares `DATA_DIR`
|
- 2026-10-03: root no longer acts outside `/data` when it prepares `DATA_DIR`
|
||||||
(issue #80): `bin/entrypoint.sh` runs `netwatch-server prepare-data-dir`,
|
(issue #80): `bin/entrypoint.sh` runs `netwatch-server prepare-data-dir`,
|
||||||
which refuses a `DATA_DIR` that is not `/data` or a path below it written in
|
which refuses a `DATA_DIR` that is not `/data` or a path below it written in
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"sneak.berlin/go/netwatch/internal/server"
|
"sneak.berlin/go/netwatch/internal/server"
|
||||||
|
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
"go.uber.org/fx/fxevent"
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:gochecknoglobals // set via ldflags at build time
|
//nolint:gochecknoglobals // set via ldflags at build time
|
||||||
@@ -62,6 +63,12 @@ func main() {
|
|||||||
globals.Version = Version
|
globals.Version = Version
|
||||||
|
|
||||||
fx.New(
|
fx.New(
|
||||||
|
// fx logs each step of starting and stopping through the
|
||||||
|
// server's own logger, so off a terminal those lines are
|
||||||
|
// JSON like every other line.
|
||||||
|
fx.WithLogger(func(log *logger.Logger) fxevent.Logger {
|
||||||
|
return &fxevent.SlogLogger{Logger: log.Get()}
|
||||||
|
}),
|
||||||
fx.Provide(
|
fx.Provide(
|
||||||
config.New,
|
config.New,
|
||||||
globals.New,
|
globals.New,
|
||||||
@@ -72,6 +79,11 @@ func main() {
|
|||||||
reportbuf.New,
|
reportbuf.New,
|
||||||
server.New,
|
server.New,
|
||||||
),
|
),
|
||||||
fx.Invoke(func(*server.Server) {}),
|
fx.Invoke(
|
||||||
|
// First, so the name and version are logged even when
|
||||||
|
// a setting stops the start.
|
||||||
|
func(log *logger.Logger) { log.Identify() },
|
||||||
|
func(*server.Server) {},
|
||||||
|
),
|
||||||
).Run()
|
).Run()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ import "net/http"
|
|||||||
// endpoint.
|
// endpoint.
|
||||||
func (s *Handlers) HandleHealthCheck() http.HandlerFunc {
|
func (s *Handlers) HandleHealthCheck() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
s.respondJSON(w, r, s.hc.Check(), http.StatusOK)
|
s.respondJSON(w, r, s.hc.Healthcheck(), http.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ func newStartedHandlers(t *testing.T, g *globals.Globals) *handlers.Handlers {
|
|||||||
|
|
||||||
// TestHandleHealthCheck checks the health check's answer: 200, a JSON
|
// TestHandleHealthCheck checks the health check's answer: 200, a JSON
|
||||||
// content type, and a JSON object with exactly the fields of
|
// content type, and a JSON object with exactly the fields of
|
||||||
// healthcheck.Response, carrying this server's name and version and
|
// healthcheck.HealthcheckResponse, carrying this server's name and
|
||||||
// an uptime counted from its start.
|
// version and an uptime counted from its start.
|
||||||
func TestHandleHealthCheck(t *testing.T) {
|
func TestHandleHealthCheck(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ func TestHandleHealthCheck(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fields := []string{
|
fields := []string{
|
||||||
"appname", "now", "status", "uptimeHuman", "uptimeSeconds", "version",
|
"appname", "now", "status", "uptime_human", "uptime_seconds", "version",
|
||||||
}
|
}
|
||||||
if got := slices.Sorted(maps.Keys(body)); !slices.Equal(got, fields) {
|
if got := slices.Sorted(maps.Keys(body)); !slices.Equal(got, fields) {
|
||||||
t.Fatalf("fields = %v, want %v", got, fields)
|
t.Fatalf("fields = %v, want %v", got, fields)
|
||||||
@@ -104,17 +104,17 @@ func TestHandleHealthCheck(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Started just now, so the uptime is well under a minute.
|
// Started just now, so the uptime is well under a minute.
|
||||||
human, _ := body["uptimeHuman"].(string)
|
human, _ := body["uptime_human"].(string)
|
||||||
|
|
||||||
uptime, err := time.ParseDuration(human)
|
uptime, err := time.ParseDuration(human)
|
||||||
if err != nil || uptime > time.Minute {
|
if err != nil || uptime > time.Minute {
|
||||||
t.Errorf("uptimeHuman = %q, want a duration under a minute (%v)",
|
t.Errorf("uptime_human = %q, want a duration under a minute (%v)",
|
||||||
human, err)
|
human, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
seconds, ok := body["uptimeSeconds"].(float64)
|
seconds, ok := body["uptime_seconds"].(float64)
|
||||||
if !ok || seconds < 0 || seconds > time.Minute.Seconds() {
|
if !ok || seconds < 0 || seconds > time.Minute.Seconds() {
|
||||||
t.Errorf("uptimeSeconds = %v, want a number of seconds under a minute",
|
t.Errorf("uptime_seconds = %v, want a number of seconds under a minute",
|
||||||
body["uptimeSeconds"])
|
body["uptime_seconds"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,14 +30,17 @@ type Healthcheck struct {
|
|||||||
params *Params
|
params *Params
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response is the JSON payload returned by the health check
|
// HealthcheckResponse is the JSON payload returned by the health
|
||||||
// endpoint.
|
// check endpoint. Its name and its snake_case keys are the ones
|
||||||
type Response struct {
|
// GO_HTTP_SERVER_CONVENTIONS.md gives.
|
||||||
|
//
|
||||||
|
//nolint:revive,tagliatelle // name and keys from the conventions
|
||||||
|
type HealthcheckResponse struct {
|
||||||
Appname string `json:"appname"`
|
Appname string `json:"appname"`
|
||||||
Now string `json:"now"`
|
Now string `json:"now"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
UptimeHuman string `json:"uptimeHuman"`
|
UptimeHuman string `json:"uptime_human"`
|
||||||
UptimeSeconds int64 `json:"uptimeSeconds"`
|
UptimeSeconds int64 `json:"uptime_seconds"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +68,9 @@ func New(
|
|||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check returns the current health status of the application.
|
// Healthcheck returns the current health status of the application.
|
||||||
func (s *Healthcheck) Check() *Response {
|
func (s *Healthcheck) Healthcheck() *HealthcheckResponse {
|
||||||
return &Response{
|
return &HealthcheckResponse{
|
||||||
Appname: s.params.Globals.Appname,
|
Appname: s.params.Globals.Appname,
|
||||||
Now: time.Now().UTC().Format(time.RFC3339Nano),
|
Now: time.Now().UTC().Format(time.RFC3339Nano),
|
||||||
Status: "ok",
|
Status: "ok",
|
||||||
|
|||||||
Reference in New Issue
Block a user