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
21 changes: 18 additions & 3 deletions apps/hub/src/provisioners/process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ import {

const HUB_WS_URL = "ws://127.0.0.1:3000/api/sidecars/ws";
const SIDECAR_ENTRY = "/srv/workbench/apps/sidecar/src/index.ts";
const SIDECAR_KEY = "22".repeat(32);

describe("readProcessProvisionerConfig", () => {
test("an unconfigured environment resolves this repository's sidecar entry and the running bun", () => {
const config = readProcessProvisionerConfig({
env: {},
env: { SIDECAR_CREDENTIAL_ENCRYPTION_KEY: SIDECAR_KEY },
dataDir: "/srv/hub-data/process-provisioner",
hubWebSocketUrl: HUB_WS_URL,
});

expect(config.sidecarEntryPath).toEndWith("/apps/sidecar/src/index.ts");
expect(config.runtimePath).toBe(process.execPath);
expect(config.sidecarCredentialEncryptionKey).toBe(SIDECAR_KEY);
expect(config.allocationsDir).toBe("/srv/hub-data/process-provisioner/allocations");
expect(config.stateFilePath).toBe("/srv/hub-data/process-provisioner/state.json");
expect(config.hubWebSocketUrl).toBe(HUB_WS_URL);
Expand All @@ -39,6 +41,7 @@ describe("readProcessProvisionerConfig", () => {
env: {
PROCESS_PROVISIONER_SIDECAR_ENTRY: "/opt/sidecar/index.js",
PROCESS_PROVISIONER_RUNTIME: "/usr/bin/node",
SIDECAR_CREDENTIAL_ENCRYPTION_KEY: SIDECAR_KEY,
},
dataDir: "/srv/hub-data/process-provisioner",
hubWebSocketUrl: HUB_WS_URL,
Expand All @@ -51,7 +54,7 @@ describe("readProcessProvisionerConfig", () => {
test("a relative data dir fails loudly instead of resolving against the cwd", () => {
expect(() =>
readProcessProvisionerConfig({
env: {},
env: { SIDECAR_CREDENTIAL_ENCRYPTION_KEY: SIDECAR_KEY },
dataDir: ".data/hub/process-provisioner",
hubWebSocketUrl: HUB_WS_URL,
}),
Expand All @@ -61,7 +64,17 @@ describe("readProcessProvisionerConfig", () => {
test("an empty override is a misconfiguration, not an unset key", () => {
expect(() =>
readProcessProvisionerConfig({
env: { PROCESS_PROVISIONER_RUNTIME: "" },
env: { PROCESS_PROVISIONER_RUNTIME: "", SIDECAR_CREDENTIAL_ENCRYPTION_KEY: SIDECAR_KEY },
dataDir: "/srv/hub-data/process-provisioner",
hubWebSocketUrl: HUB_WS_URL,
}),
).toThrow("invalid process provisioner environment");
});

test("a hub without the sidecar key cannot provision at all", () => {
expect(() =>
readProcessProvisionerConfig({
env: {},
dataDir: "/srv/hub-data/process-provisioner",
hubWebSocketUrl: HUB_WS_URL,
}),
Expand All @@ -76,6 +89,7 @@ async function configIn(dataDir: string): Promise<ProcessProvisionerConfig> {
allocationsDir: resolve(dataDir, "allocations"),
stateFilePath: resolve(dataDir, "state.json"),
hubWebSocketUrl: HUB_WS_URL,
sidecarCredentialEncryptionKey: SIDECAR_KEY,
};
}

Expand Down Expand Up @@ -131,6 +145,7 @@ describe("createProcessSidecarProvisioner", () => {
resolve(config.allocationsDir, "alloc-1", "gen-1", "data"),
);
expect(spawn?.env["PATH"]).toBe(process.env["PATH"]);
expect(spawn?.env["SIDECAR_CREDENTIAL_ENCRYPTION_KEY"]).toBe(SIDECAR_KEY);
});

test("ensure records the pid so a restarted hub can still find the unit", async () => {
Expand Down
15 changes: 12 additions & 3 deletions apps/hub/src/provisioners/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const DEFAULT_SIDECAR_ENTRY = resolve(
const Environment = type({
"PROCESS_PROVISIONER_SIDECAR_ENTRY?": "string > 0",
"PROCESS_PROVISIONER_RUNTIME?": "string > 0",
SIDECAR_CREDENTIAL_ENCRYPTION_KEY: "string > 0",
});

export type ProcessProvisionerConfig = {
Expand All @@ -54,6 +55,8 @@ export type ProcessProvisionerConfig = {
* binding fingerprint, since a hub moved to a new address is a
* different backend binding even with the same entry point. */
readonly hubWebSocketUrl: string;
/** The operator key every spawned sidecar seals its at-rest credentials under. */
readonly sidecarCredentialEncryptionKey: string;
};

export type ReadProcessProvisionerConfigArgs = {
Expand All @@ -67,7 +70,7 @@ export type ReadProcessProvisionerConfigArgs = {
readonly hubWebSocketUrl: string;
};

/** Both env keys are optional: unconfigured, this spawns the repo's own apps/sidecar with the running bun. */
/** The entry and runtime keys are optional: unconfigured, this spawns the repo's own apps/sidecar with the running bun. */
export function readProcessProvisionerConfig(
args: ReadProcessProvisionerConfigArgs,
): ProcessProvisionerConfig {
Expand All @@ -89,6 +92,7 @@ export function readProcessProvisionerConfig(
allocationsDir: resolve(args.dataDir, "allocations"),
stateFilePath: resolve(args.dataDir, "state.json"),
hubWebSocketUrl: args.hubWebSocketUrl,
sidecarCredentialEncryptionKey: parsed.SIDECAR_CREDENTIAL_ENCRYPTION_KEY,
};
}

Expand Down Expand Up @@ -184,7 +188,7 @@ export function createProcessBackend(
async startUnit(args: StartUnitArgs): Promise<string> {
const unitDir = unitDirOf(args.allocationId, args.generation);
const sidecarDataDir = resolve(unitDir, SIDECAR_DATA_DIR_NAME);
const env = sidecarEnvFor(args, sidecarDataDir);
const env = sidecarEnvFor(args, sidecarDataDir, config.sidecarCredentialEncryptionKey);
try {
await mkdir(sidecarDataDir, { recursive: true, mode: 0o700 });
} catch (error) {
Expand Down Expand Up @@ -293,7 +297,11 @@ export function createProcessBackend(
}

/** A sidecar learns everything else over the wire; nothing else of the hub's env is inherited. */
function sidecarEnvFor(args: StartUnitArgs, sidecarDataDir: string): Record<string, string> {
function sidecarEnvFor(
args: StartUnitArgs,
sidecarDataDir: string,
sidecarCredentialEncryptionKey: string,
): Record<string, string> {
const path = process.env["PATH"];
if (path === undefined || path === "") {
const error = new Error(
Expand All @@ -312,6 +320,7 @@ function sidecarEnvFor(args: StartUnitArgs, sidecarDataDir: string): Record<stri
HUB_WS_URL: args.hubWebSocketUrl,
SIDECAR_ID: args.sidecarId,
SIDECAR_TOKEN: args.token,
SIDECAR_CREDENTIAL_ENCRYPTION_KEY: sidecarCredentialEncryptionKey,
PATH: path,
...(home === undefined ? {} : { HOME: home }),
...(tmpdir === undefined ? {} : { TMPDIR: tmpdir }),
Expand Down
Loading