Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmd/epmon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ func runDefault(args []string, stdout, stderr io.Writer) int {

root := http.NewServeMux()
root.Handle("/metrics", registry.Handler())
root.Handle("/", api.New(cfg, st, nil).Handler())
apiSrv := api.New(cfg, st, nil)
apiSrv.SetVersion(version)
root.Handle("/", apiSrv.Handler())

srv := newHTTPServer(cfg, api.Log(root))
// serveErr carries a bind/serve failure back to runDefault so boot can
Expand Down
14 changes: 11 additions & 3 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,16 @@ var openAPIYAML []byte
// Server bundles config and storage for the handlers. Storage is the
// store.Store port — any adapter (SQLite, Postgres, …) plugs in here.
type Server struct {
cfg *config.Config
store store.Store
now func() time.Time
cfg *config.Config
store store.Store
now func() time.Time
version string
}

// SetVersion stamps build identity for the /healthz version header
// (see SECURITY.md reporting flow). Empty means "omit the header".
func (s *Server) SetVersion(v string) { s.version = v }

// New builds a Server. now is injectable for tests (nil = time.Now).
func New(cfg *config.Config, st store.Store, now func() time.Time) *Server {
if now == nil {
Expand Down Expand Up @@ -169,6 +174,9 @@ func (s *Server) healthz(w http.ResponseWriter, r *http.Request) {
writeErr(w, http.StatusServiceUnavailable, "unavailable", "store unreachable")
return
}
if s.version != "" {
w.Header().Set("X-Epmon-Version", s.version)
}
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
}

Expand Down
22 changes: 22 additions & 0 deletions internal/api/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,28 @@ func TestHealthzBoundedPing(t *testing.T) {
}
}

// TestHealthzVersionHeader asserts the SECURITY.md reporting flow:
// a stamped server reports its build on /healthz, an unstamped one
// omits the header entirely.
func TestHealthzVersionHeader(t *testing.T) {
srv, _ := testServer(t)
srv.SetVersion("v9.9.9")
h := srv.Handler()

rec := httptest.NewRecorder()
h.ServeHTTP(rec, httptest.NewRequest("GET", "/healthz", nil))
if got := rec.Header().Get("X-Epmon-Version"); got != "v9.9.9" {
t.Errorf("X-Epmon-Version = %q, want v9.9.9", got)
}

plain, _ := testServer(t)
rec = httptest.NewRecorder()
plain.Handler().ServeHTTP(rec, httptest.NewRequest("GET", "/healthz", nil))
if got := rec.Header().Get("X-Epmon-Version"); got != "" {
t.Errorf("X-Epmon-Version = %q, want absent when unset", got)
}
}

func TestHealthzReadiness(t *testing.T) {
srv, st := testServer(t)
h := srv.Handler()
Expand Down
Loading