mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-24 12:54:26 +00:00
Secrets: make runtime activation auth loads read-only
This commit is contained in:
committed by
Peter Steinberger
parent
3dbb6be270
commit
8e33ebe471
@@ -1,4 +1,7 @@
|
||||
import fs from "node:fs/promises";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { resolveMainSessionKeyFromConfig } from "../config/sessions.js";
|
||||
import { drainSystemEvents } from "../infra/system-events.js";
|
||||
import {
|
||||
connectOk,
|
||||
installGatewayTestHooks,
|
||||
@@ -170,11 +173,13 @@ describe("gateway hot reload", () => {
|
||||
let prevSkipChannels: string | undefined;
|
||||
let prevSkipGmail: string | undefined;
|
||||
let prevSkipProviders: string | undefined;
|
||||
let prevOpenAiApiKey: string | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
prevSkipChannels = process.env.OPENCLAW_SKIP_CHANNELS;
|
||||
prevSkipGmail = process.env.OPENCLAW_SKIP_GMAIL_WATCHER;
|
||||
prevSkipProviders = process.env.OPENCLAW_SKIP_PROVIDERS;
|
||||
prevOpenAiApiKey = process.env.OPENAI_API_KEY;
|
||||
process.env.OPENCLAW_SKIP_CHANNELS = "0";
|
||||
delete process.env.OPENCLAW_SKIP_GMAIL_WATCHER;
|
||||
delete process.env.OPENCLAW_SKIP_PROVIDERS;
|
||||
@@ -196,8 +201,39 @@ describe("gateway hot reload", () => {
|
||||
} else {
|
||||
process.env.OPENCLAW_SKIP_PROVIDERS = prevSkipProviders;
|
||||
}
|
||||
if (prevOpenAiApiKey === undefined) {
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
} else {
|
||||
process.env.OPENAI_API_KEY = prevOpenAiApiKey;
|
||||
}
|
||||
});
|
||||
|
||||
async function writeEnvRefConfig() {
|
||||
const configPath = process.env.OPENCLAW_CONFIG_PATH;
|
||||
if (!configPath) {
|
||||
throw new Error("OPENCLAW_CONFIG_PATH is not set");
|
||||
}
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: { source: "env", id: "OPENAI_API_KEY" },
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
|
||||
it("applies hot reload actions and emits restart signal", async () => {
|
||||
await withGatewayServer(async () => {
|
||||
const onHotReload = hoisted.getOnHotReload();
|
||||
@@ -302,6 +338,70 @@ describe("gateway hot reload", () => {
|
||||
expect(signalSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it("fails startup when required secret refs are unresolved", async () => {
|
||||
await writeEnvRefConfig();
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
await expect(withGatewayServer(async () => {})).rejects.toThrow(
|
||||
"Startup failed: required secrets are unavailable",
|
||||
);
|
||||
});
|
||||
|
||||
it("emits one-shot degraded and recovered system events during secret reload transitions", async () => {
|
||||
await writeEnvRefConfig();
|
||||
process.env.OPENAI_API_KEY = "sk-startup";
|
||||
|
||||
await withGatewayServer(async () => {
|
||||
const onHotReload = hoisted.getOnHotReload();
|
||||
expect(onHotReload).toBeTypeOf("function");
|
||||
const sessionKey = resolveMainSessionKeyFromConfig();
|
||||
const plan = {
|
||||
changedPaths: ["models.providers.openai.apiKey"],
|
||||
restartGateway: false,
|
||||
restartReasons: [],
|
||||
hotReasons: ["models.providers.openai.apiKey"],
|
||||
reloadHooks: false,
|
||||
restartGmailWatcher: false,
|
||||
restartBrowserControl: false,
|
||||
restartCron: false,
|
||||
restartHeartbeat: false,
|
||||
restartChannels: new Set(),
|
||||
noopPaths: [],
|
||||
};
|
||||
const nextConfig = {
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: { source: "env", id: "OPENAI_API_KEY" },
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
await expect(onHotReload?.(plan, nextConfig)).rejects.toThrow(
|
||||
'Environment variable "OPENAI_API_KEY" is missing or empty.',
|
||||
);
|
||||
const degradedEvents = drainSystemEvents(sessionKey);
|
||||
expect(degradedEvents.some((event) => event.includes("[SECRETS_RELOADER_DEGRADED]"))).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
await expect(onHotReload?.(plan, nextConfig)).rejects.toThrow(
|
||||
'Environment variable "OPENAI_API_KEY" is missing or empty.',
|
||||
);
|
||||
expect(drainSystemEvents(sessionKey)).toEqual([]);
|
||||
|
||||
process.env.OPENAI_API_KEY = "sk-recovered";
|
||||
await expect(onHotReload?.(plan, nextConfig)).resolves.toBeUndefined();
|
||||
const recoveredEvents = drainSystemEvents(sessionKey);
|
||||
expect(recoveredEvents.some((event) => event.includes("[SECRETS_RELOADER_RECOVERED]"))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("gateway agents", () => {
|
||||
|
||||
Reference in New Issue
Block a user