Public: anyone can read this; the crew can change it.

/healthz says which version is serving, in an X-Forge-Version header #2293

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/2281-healthz-says-which-version-is-serving-in → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/2281-healthz-says-which-version-is-serving-in was merged into main as fd43605.

Review

Sign in to review this change.
2 files changed +13 −1
src/http/server.ts +5 −1
@@ -62,6 +62,7 @@ import {
6262 } from "../core/users.ts";
6363 import { edgeIdentity } from "./edge.ts";
6464 import { liveResponse, readyResponse } from "./health.ts";
65 import { running } from "../kernel/version.ts";
6566 import { runGit } from "../git/env.ts";
6667 import { noteRefusedInternal } from "../kernel/internal_refused.ts";
6768 import { pushCap, storageChanged, storageRefusal, storageRefusedText, storageSentence } from "../core/storage.ts";
@@ -2226,7 +2227,10 @@ async function answerGet(
22262227 const url = publicUrl(request, app.config.url);
22272228 if (url.pathname === "/healthz") {
22282229 noteUsage(request, { pattern: "/healthz", role: "anon" });
2229 return liveResponse(() => app.db.query("select 1").get(), app.log);
2230 const live = liveResponse(() => app.db.query("select 1").get(), app.log);
2231 // A smoke test learns which release answered, old pod or new.
2232 live.headers.set("X-Forge-Version", running().version);

Fine as is. running() re-reads env per probe, which is negligible. The value passes the VERSION allowlist regex, so header injection is not possible.

2233 return live;
22302234 }
22312235 if (url.pathname === "/readyz") {
22322236 noteUsage(request, { pattern: "/readyz", role: "anon" });
test/health.test.ts +8
@@ -13,6 +13,7 @@ import {
1313 startDraining,
1414 } from "../src/http/health.ts";
1515 import { serve, type Servers } from "../src/http/server.ts";
16 import { running } from "../src/kernel/version.ts";
1617 import type { Log, LogFields } from "../src/kernel/log.ts";
1718
1819 /** A log that keeps its lines, so a test can read what was said. */
@@ -102,6 +103,13 @@ describe("the served paths", () => {
102103 await rm(dataDir, { recursive: true, force: true });
103104 });
104105
106 test("/healthz names the running version and keeps the security headers", async () => {
107 const res = await fetch(`${servers.url}/healthz`);
108 expect(res.headers.get("x-forge-version")).toBe(running().version);
109 expect(res.headers.get("x-content-type-options")).toBe("nosniff");
110 expect(res.headers.get("content-security-policy")).toBeTruthy();
111 });
112
105113 test("a slow database query (fake) does not turn /healthz into 503", async () => {
106114 const db = app.db as unknown as { query: (sql: string) => unknown };
107115 const query = db.query;

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs