Gateway: serialize secrets activation across reload paths
This commit is contained in:
committed by
Peter Steinberger
parent
fe56700026
commit
2e53033f22
@@ -270,49 +270,59 @@ export async function startGatewayServer(
|
||||
contextKey: code,
|
||||
});
|
||||
};
|
||||
let secretsActivationTail: Promise<void> = Promise.resolve();
|
||||
const runWithSecretsActivationLock = async <T>(operation: () => Promise<T>): Promise<T> => {
|
||||
const run = secretsActivationTail.then(operation, operation);
|
||||
secretsActivationTail = run.then(
|
||||
() => undefined,
|
||||
() => undefined,
|
||||
);
|
||||
return await run;
|
||||
};
|
||||
const activateRuntimeSecrets = async (
|
||||
config: OpenClawConfig,
|
||||
params: { reason: "startup" | "reload" | "restart-check"; activate: boolean },
|
||||
) => {
|
||||
try {
|
||||
const prepared = await prepareSecretsRuntimeSnapshot({ config });
|
||||
if (params.activate) {
|
||||
activateSecretsRuntimeSnapshot(prepared);
|
||||
}
|
||||
for (const warning of prepared.warnings) {
|
||||
logSecrets.warn(`[${warning.code}] ${warning.message}`);
|
||||
}
|
||||
if (secretsDegraded) {
|
||||
const recoveredMessage =
|
||||
"Secret resolution recovered; runtime remained on last-known-good during the outage.";
|
||||
logSecrets.info(`[SECRETS_RELOADER_RECOVERED] ${recoveredMessage}`);
|
||||
emitSecretsStateEvent("SECRETS_RELOADER_RECOVERED", recoveredMessage, prepared.config);
|
||||
}
|
||||
secretsDegraded = false;
|
||||
return prepared;
|
||||
} catch (err) {
|
||||
const details = String(err);
|
||||
if (!secretsDegraded) {
|
||||
logSecrets.error(`[SECRETS_RELOADER_DEGRADED] ${details}`);
|
||||
if (params.reason !== "startup") {
|
||||
emitSecretsStateEvent(
|
||||
"SECRETS_RELOADER_DEGRADED",
|
||||
`Secret resolution failed; runtime remains on last-known-good snapshot. ${details}`,
|
||||
config,
|
||||
);
|
||||
) =>
|
||||
await runWithSecretsActivationLock(async () => {
|
||||
try {
|
||||
const prepared = await prepareSecretsRuntimeSnapshot({ config });
|
||||
if (params.activate) {
|
||||
activateSecretsRuntimeSnapshot(prepared);
|
||||
}
|
||||
} else {
|
||||
logSecrets.warn(`[SECRETS_RELOADER_DEGRADED] ${details}`);
|
||||
for (const warning of prepared.warnings) {
|
||||
logSecrets.warn(`[${warning.code}] ${warning.message}`);
|
||||
}
|
||||
if (secretsDegraded) {
|
||||
const recoveredMessage =
|
||||
"Secret resolution recovered; runtime remained on last-known-good during the outage.";
|
||||
logSecrets.info(`[SECRETS_RELOADER_RECOVERED] ${recoveredMessage}`);
|
||||
emitSecretsStateEvent("SECRETS_RELOADER_RECOVERED", recoveredMessage, prepared.config);
|
||||
}
|
||||
secretsDegraded = false;
|
||||
return prepared;
|
||||
} catch (err) {
|
||||
const details = String(err);
|
||||
if (!secretsDegraded) {
|
||||
logSecrets.error(`[SECRETS_RELOADER_DEGRADED] ${details}`);
|
||||
if (params.reason !== "startup") {
|
||||
emitSecretsStateEvent(
|
||||
"SECRETS_RELOADER_DEGRADED",
|
||||
`Secret resolution failed; runtime remains on last-known-good snapshot. ${details}`,
|
||||
config,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
logSecrets.warn(`[SECRETS_RELOADER_DEGRADED] ${details}`);
|
||||
}
|
||||
secretsDegraded = true;
|
||||
if (params.reason === "startup") {
|
||||
throw new Error(`Startup failed: required secrets are unavailable. ${details}`, {
|
||||
cause: err,
|
||||
});
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
secretsDegraded = true;
|
||||
if (params.reason === "startup") {
|
||||
throw new Error(`Startup failed: required secrets are unavailable. ${details}`, {
|
||||
cause: err,
|
||||
});
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Fail fast before startup if required refs are unresolved.
|
||||
let cfgAtStart: OpenClawConfig;
|
||||
|
||||
Reference in New Issue
Block a user