Skip to content
Closed
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
26 changes: 22 additions & 4 deletions control-plane/src/container-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ export type ContainerDriverConfig = {
* `product` string `TenantProvisioningRequest` carries; an unconfigured product is a real
* misconfiguration, not a silent no-op (see `bindingFor`). */
bindings: Record<Product, ContainerNamespaceLike>;
/** The hosted fleet's central Sentry DSN (#7876), injected into every tenant container at cold boot as
* {@link SENTRY_DSN_ENV_VAR} so its self-host process reports to it. Omitted/blank ⇒ containers start with
* no injected DSN, byte-identical to the pre-#7876 call — the platform simply isn't reporting yet (e.g.
* before #7875 provisions the secret). Sourced from the control-plane's own env in driver-factory.ts. */
centralSentryDsn?: string;
};

export type ContainerDriver = {
Expand Down Expand Up @@ -75,21 +80,34 @@ export const PINNED_VERSION_ENV_VAR = "LOOPOVER_PINNED_VERSION";
* actually has custodied -- this driver never sees or needs to know what that is. */
export const TENANT_SECRET_ENV_VAR = "LOOPOVER_TENANT_SECRET_TOKEN";

/** The env var a tenant's container reads its Sentry DSN from at cold boot (#7876, implements #4934). A tenant
* container runs an unmodified self-host image (root Dockerfile for ORB; packages/loopover-miner/Dockerfile
* for AMS), whose process already inits error reporting from this exact var via the opt-in `initSentry`
* pattern (`src/selfhost/sentry.ts`: a complete no-op when unset) — so pointing the hosted fleet at the
* central DSN is purely INJECTING that value here, never a second Sentry-wiring mechanism. Deliberately the
* self-host name `SENTRY_DSN` (not a hosted-only alias): the image's own init reads this and only this, and a
* self-hoster who sets their own `SENTRY_DSN` is completely unaffected — this only supplies a value the
* hosted platform would otherwise leave unset. The value is a control-plane secret (provisioned by #7875),
* never hardcoded or committed. */
export const SENTRY_DSN_ENV_VAR = "SENTRY_DSN";

/** Idempotent: an already-provisioned tenant's container is left running as-is, never restarted -- a repeat
* create must not interrupt a container mid-work. This is also the ONLY point in a tenant's lifecycle where
* `envVars` actually reach the container (confirmed against the real `@cloudflare/containers` SDK: a `start()`
* call against an already-running/starting instance is a no-op or throws, never re-applies `envVars`) -- so
* both of the values below must already be known by the time this runs, not supplied later. A tenant with a
* `pinnedVersion` (#4898) starts with that version in {@link PINNED_VERSION_ENV_VAR}; one with a
* all three of the values below must already be known by the time this runs, not supplied later. A tenant with
* a `pinnedVersion` (#4898) starts with that version in {@link PINNED_VERSION_ENV_VAR}; one with a
* `bootstrapSecret` (#8202, set on `request` by `provisionTenant` from `injectSecrets`' result) starts with it
* in {@link TENANT_SECRET_ENV_VAR}; a tenant with neither gets the exact pre-#4898 `start()` call, so every
* existing tenant's behavior is byte-identical until either rollout applies. */
* in {@link TENANT_SECRET_ENV_VAR}; and when the config carries a central Sentry DSN (#7876) every container
* starts with it in {@link SENTRY_DSN_ENV_VAR}. A tenant with none of them gets the exact pre-#4898 `start()`
* call, so every existing tenant's behavior is byte-identical until a rollout applies. */
export async function createTenantContainer(config: ContainerDriverConfig, request: TenantProvisioningRequest): Promise<void> {
const stub = bindingFor(config, request.product).getByName(instanceNameFor(request));
if (await stub.isProvisioned()) return;
const envVars: Record<string, string> = {};
if (request.tenant.pinnedVersion) envVars[PINNED_VERSION_ENV_VAR] = request.tenant.pinnedVersion;
if (request.bootstrapSecret) envVars[TENANT_SECRET_ENV_VAR] = request.bootstrapSecret;
if (config.centralSentryDsn) envVars[SENTRY_DSN_ENV_VAR] = config.centralSentryDsn;
if (Object.keys(envVars).length > 0) {
await stub.start({ envVars });
} else {
Expand Down
7 changes: 6 additions & 1 deletion control-plane/src/driver-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@ export function createTenantProvisioningDriver(
}

if (containerBindings && Object.keys(containerBindings).length > 0) {
driver = withRealContainerDriver(driver, createContainerDriver({ bindings: containerBindings }));
// #7876: point every tenant container at the hosted fleet's central Sentry DSN when the control-plane
// secret is set (per #7875). Blank/unset ⇒ omitted, so containers start byte-identical to pre-#7876.
driver = withRealContainerDriver(
driver,
createContainerDriver({ bindings: containerBindings, ...(nonBlank(env.SENTRY_DSN) ? { centralSentryDsn: nonBlank(env.SENTRY_DSN) } : {}) }),
);
}

const mainAppBaseUrl = nonBlank(env.MAIN_APP_BASE_URL);
Expand Down
6 changes: 6 additions & 0 deletions control-plane/src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ declare global {
* (a plain var, see wrangler.jsonc); createTenantProvisioningDriver falls back to the fake otherwise.
* Genuinely sensitive: whoever holds it can call every internal admin route in the main app. */
INTERNAL_JOB_TOKEN?: string;
/** The hosted fleet's central Sentry DSN (#7876, implements #4934), provisioned as a secret by #7875.
* createTenantProvisioningDriver injects it into every tenant container as `SENTRY_DSN` so the container's
* own self-host process reports to it (`src/selfhost/sentry.ts`'s opt-in init). Unset ⇒ containers start
* with no injected DSN, byte-identical to today. A DSN is a write-only ingest URL rather than a
* credential, but it's kept a secret (never a committed var) so the hosted ingest endpoint isn't public. */
SENTRY_DSN?: string;
}
}

Expand Down
1 change: 1 addition & 0 deletions control-plane/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export {
instanceNameFor,
PINNED_VERSION_ENV_VAR,
TENANT_SECRET_ENV_VAR,
SENTRY_DSN_ENV_VAR,
tenantContainerExists,
type ContainerDriver,
type ContainerDriverConfig,
Expand Down
4 changes: 3 additions & 1 deletion control-plane/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ export default {
const driver = createTenantProvisioningDriver(
// #8066: MAIN_APP_BASE_URL/INTERNAL_JOB_TOKEN select the real secret driver (against the main app's
// token broker) once both are configured, same opt-in-per-piece shape as NEON_API_KEY/NEON_PROJECT_ID.
{ NEON_API_KEY: env.NEON_API_KEY, NEON_PROJECT_ID: env.NEON_PROJECT_ID, MAIN_APP_BASE_URL: env.MAIN_APP_BASE_URL, INTERNAL_JOB_TOKEN: env.INTERNAL_JOB_TOKEN },
// #7876: SENTRY_DSN is the hosted fleet's central DSN (a control-plane secret, #7875) — the factory
// injects it into every tenant container so its self-host process reports to it (unset ⇒ no-op).
{ NEON_API_KEY: env.NEON_API_KEY, NEON_PROJECT_ID: env.NEON_PROJECT_ID, MAIN_APP_BASE_URL: env.MAIN_APP_BASE_URL, INTERNAL_JOB_TOKEN: env.INTERNAL_JOB_TOKEN, SENTRY_DSN: env.SENTRY_DSN },
{ orb: env.ORB_TENANT_CONTAINER, ams: env.AMS_TENANT_CONTAINER },
);
const app = createTenantHttpApp({
Expand Down
44 changes: 44 additions & 0 deletions control-plane/test/container-driver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
destroyTenantContainer,
PINNED_VERSION_ENV_VAR,
TENANT_SECRET_ENV_VAR,
SENTRY_DSN_ENV_VAR,
tenantContainerExists,
type ContainerDriverConfig,
type ContainerNamespaceLike,
Expand Down Expand Up @@ -224,3 +225,46 @@ test("a repeat create of an already-provisioned tenant with a bootstrap secret n

assert.deepEqual(stub.startOptions, []);
});

// #7876: the hosted fleet's central Sentry DSN rides into every tenant container at cold boot as
// SENTRY_DSN_ENV_VAR — the exact self-host var its own process reads via src/selfhost/sentry.ts's opt-in
// init, so pointing the fleet at central telemetry is purely injecting the value, not a second mechanism.
function configWithDsn(stub: ContainerStubLike, centralSentryDsn: string): ContainerDriverConfig {
return { bindings: { orb: { getByName: () => stub } }, centralSentryDsn };
}

test("a container starts with SENTRY_DSN_ENV_VAR when the config carries the central DSN (#7876)", async () => {
const stub = optionCapturingStub();

await createTenantContainer(configWithDsn(stub, "https://k@o0.ingest.sentry.io/1"), { tenant: { name: "acme" }, product: "orb" });

assert.deepEqual(stub.startOptions, [{ envVars: { [SENTRY_DSN_ENV_VAR]: "https://k@o0.ingest.sentry.io/1" } }]);
});

test("the central DSN merges with pinnedVersion + bootstrapSecret into one start() call (#7876)", async () => {
const stub = optionCapturingStub();

await createTenantContainer(configWithDsn(stub, "https://k@o0.ingest.sentry.io/1"), {
tenant: { name: "acme", pinnedVersion: "v1.4.2" },
product: "orb",
bootstrapSecret: "orbsec_xyz",
});

assert.deepEqual(stub.startOptions, [
{
envVars: {
[PINNED_VERSION_ENV_VAR]: "v1.4.2",
[TENANT_SECRET_ENV_VAR]: "orbsec_xyz",
[SENTRY_DSN_ENV_VAR]: "https://k@o0.ingest.sentry.io/1",
},
},
]);
});

test("a config with no central DSN never injects SENTRY_DSN — byte-identical to today for an otherwise-plain tenant (#7876)", async () => {
const stub = optionCapturingStub();

await createTenantContainer(configFor(stub), { tenant: { name: "acme" }, product: "orb" });

assert.deepEqual(stub.startOptions, [undefined]);
});
47 changes: 47 additions & 0 deletions control-plane/test/driver-factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { afterEach, beforeEach, test } from "node:test";
import {
createFakeTenantProvisioningDriver,
createTenantProvisioningDriver,
SENTRY_DSN_ENV_VAR,
withRealContainerDriver,
withRealDatabaseDriver,
withRealSecretDriver,
Expand All @@ -17,6 +18,32 @@ import {
type TenantProvisioningRequest,
} from "../dist/index.js";

/** A container namespace whose stub records each `start()` call's options — lets a factory test assert what
* the composed container driver actually injects (the shared fake above deliberately discards options). */
function capturingContainerNamespace(): ContainerNamespaceLike & { startOptions: Parameters<ContainerStubLike["start"]>[0][] } {
const startOptions: Parameters<ContainerStubLike["start"]>[0][] = [];
let flag = false;
const stub: ContainerStubLike = {
async start(options) {
startOptions.push(options);
flag = true;
},
async stop() {
flag = false;
},
async isProvisioned() {
return flag;
},
async markProvisioned() {
flag = true;
},
async markDeprovisioned() {
flag = false;
},
};
return { getByName: () => stub, startOptions };
}

function fakeContainerNamespace(provisioned = false): ContainerNamespaceLike {
let flag = provisioned;
const stub: ContainerStubLike = {
Expand Down Expand Up @@ -172,6 +199,26 @@ test("createTenantProvisioningDriver: selects the real container driver when con
assert.equal(await driver.containerExists(REQUEST), true);
});

test("createTenantProvisioningDriver: injects env.SENTRY_DSN into tenant containers when set (#7876)", async () => {
const orb = capturingContainerNamespace();
const driver = createTenantProvisioningDriver({ SENTRY_DSN: "https://k@o0.ingest.sentry.io/1" }, { orb });

await driver.createContainer(REQUEST);

assert.deepEqual(orb.startOptions, [{ envVars: { [SENTRY_DSN_ENV_VAR]: "https://k@o0.ingest.sentry.io/1" } }]);
});

test("createTenantProvisioningDriver: a blank or unset SENTRY_DSN injects nothing — containers start as before (#7876)", async () => {
for (const env of [{}, { SENTRY_DSN: "" }, { SENTRY_DSN: " " }]) {
const orb = capturingContainerNamespace();
const driver = createTenantProvisioningDriver(env, { orb });

await driver.createContainer(REQUEST);

assert.deepEqual(orb.startOptions, [undefined]);
}
});

test("createTenantProvisioningDriver: composes both the real database driver AND the real container driver together", async () => {
globalThis.fetch = (async () => new Response(JSON.stringify({ branches: [] }), { status: 200 })) as unknown as typeof fetch;

Expand Down