refactor: source codex sentinel names from yaml

This commit is contained in:
Codex
2026-06-14 07:19:03 +00:00
parent 72aa29317e
commit 0f826f12d3
3 changed files with 36 additions and 27 deletions
@@ -187,6 +187,8 @@ sentinel:
credentialsSecretName: sub2api-account-sentinel-profiles
stateConfigMapName: sub2api-account-sentinel-state
cronJobName: sub2api-account-sentinel
roleName: sub2api-account-sentinel
roleBindingName: sub2api-account-sentinel
model: gpt-5.5
endpoint: responses
marker:
@@ -14,6 +14,8 @@ export interface CodexPoolSentinelConfig {
credentialsSecretName: string;
stateConfigMapName: string;
cronJobName: string;
roleName: string;
roleBindingName: string;
model: string;
endpoint: "responses";
marker: {
@@ -110,6 +112,8 @@ export function defaultCodexPoolSentinelConfig(): CodexPoolSentinelConfig {
credentialsSecretName: "sub2api-account-sentinel-profiles",
stateConfigMapName: "sub2api-account-sentinel-state",
cronJobName: "sub2api-account-sentinel",
roleName: "sub2api-account-sentinel",
roleBindingName: "sub2api-account-sentinel",
model: "gpt-5.5",
endpoint: "responses",
marker: {
@@ -199,6 +203,8 @@ export function readCodexPoolSentinelConfig(value: unknown, defaults: CodexPoolS
credentialsSecretName: readDnsName(valueAt(value, "credentialsSecretName"), `${sourcePath}.sentinel.credentialsSecretName`, defaults.credentialsSecretName),
stateConfigMapName: readDnsName(valueAt(value, "stateConfigMapName"), `${sourcePath}.sentinel.stateConfigMapName`, defaults.stateConfigMapName),
cronJobName: readDnsName(valueAt(value, "cronJobName"), `${sourcePath}.sentinel.cronJobName`, defaults.cronJobName),
roleName: readRequiredDnsName(valueAt(value, "roleName"), `${sourcePath}.sentinel.roleName`),
roleBindingName: readRequiredDnsName(valueAt(value, "roleBindingName"), `${sourcePath}.sentinel.roleBindingName`),
model: readModelName(valueAt(value, "model"), `${sourcePath}.sentinel.model`, defaults.model),
endpoint: "responses",
marker: {
@@ -276,6 +282,8 @@ export function codexPoolSentinelSummary(config: CodexPoolSentinelConfig): Recor
actionsEnabled: config.actions.enabled,
schedule: config.schedule,
cronJobName: config.cronJobName,
roleName: config.roleName,
roleBindingName: config.roleBindingName,
configMapName: config.configMapName,
credentialsSecretName: config.credentialsSecretName,
stateConfigMapName: config.stateConfigMapName,
@@ -391,7 +399,7 @@ metadata:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ${config.serviceAccountName}
name: ${config.roleName}
namespace: ${options.namespace}
labels:
app.kubernetes.io/name: ${config.cronJobName}
@@ -411,7 +419,7 @@ rules:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ${config.serviceAccountName}
name: ${config.roleBindingName}
namespace: ${options.namespace}
labels:
app.kubernetes.io/name: ${config.cronJobName}
@@ -423,7 +431,7 @@ subjects:
namespace: ${options.namespace}
roleRef:
kind: Role
name: ${config.serviceAccountName}
name: ${config.roleName}
apiGroup: rbac.authorization.k8s.io
---
apiVersion: batch/v1
@@ -2097,6 +2105,15 @@ function readDnsName(value: unknown, key: string, fallback: string): string {
return text;
}
function readRequiredDnsName(value: unknown, key: string): string {
if (value === undefined || value === null) throw new Error(`${key} is required`);
if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${key} must be a non-empty string`);
if (/[\r\n]/u.test(value)) throw new Error(`${key} must not contain newlines`);
const text = value.trim();
if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/u.test(text)) throw new Error(`${key} must be a Kubernetes DNS label`);
return text;
}
function readModelName(value: unknown, key: string, fallback: string): string {
const text = readString(value, key, fallback);
if (!/^[A-Za-z0-9._:-]+$/u.test(text)) throw new Error(`${key} has an unsupported model name`);
+14 -24
View File
@@ -1023,36 +1023,26 @@ function managedResourceCleanupPlan(sub2api: Sub2ApiConfig, target: Sub2ApiTarge
}
function codexPoolSentinelResourceNames(): ManagedResourceCleanupPlan["sentinel"] {
const defaults: ManagedResourceCleanupPlan["sentinel"] = {
enabled: false,
cronJobName: "sub2api-account-sentinel",
configMapName: "sub2api-account-sentinel-config",
credentialsSecretName: "sub2api-account-sentinel-profiles",
stateConfigMapName: "sub2api-account-sentinel-state",
serviceAccountName: "sub2api-account-sentinel",
roleName: "sub2api-account-sentinel",
roleBindingName: "sub2api-account-sentinel",
};
if (!existsSync(codexPoolConfigPath)) return defaults;
if (!existsSync(codexPoolConfigPath)) throw new Error(`${codexPoolConfigPath} is required for Sub2API codex-pool sentinel cleanup resource names`);
const parsed = Bun.YAML.parse(readFileSync(codexPoolConfigPath, "utf8")) as unknown;
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return defaults;
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error(`${codexPoolConfigPath} must be an object`);
const sentinel = (parsed as Record<string, unknown>).sentinel;
if (typeof sentinel !== "object" || sentinel === null || Array.isArray(sentinel)) return defaults;
if (typeof sentinel !== "object" || sentinel === null || Array.isArray(sentinel)) throw new Error(`${codexPoolConfigPath}.sentinel must be an object`);
const record = sentinel as Record<string, unknown>;
const text = (key: string, fallback: string): string => {
const value = record[key];
return typeof value === "string" && value.length > 0 ? value : fallback;
const resourceName = (key: string): string => {
const value = stringField(record, key, "sentinel");
validateKubernetesResourceName(value, `sentinel.${key}`);
return value;
};
const serviceAccountName = text("serviceAccountName", defaults.serviceAccountName);
return {
enabled: false,
cronJobName: text("cronJobName", defaults.cronJobName),
configMapName: text("configMapName", defaults.configMapName),
credentialsSecretName: text("credentialsSecretName", defaults.credentialsSecretName),
stateConfigMapName: text("stateConfigMapName", defaults.stateConfigMapName),
serviceAccountName,
roleName: serviceAccountName,
roleBindingName: serviceAccountName,
cronJobName: resourceName("cronJobName"),
configMapName: resourceName("configMapName"),
credentialsSecretName: resourceName("credentialsSecretName"),
stateConfigMapName: resourceName("stateConfigMapName"),
serviceAccountName: resourceName("serviceAccountName"),
roleName: resourceName("roleName"),
roleBindingName: resourceName("roleBindingName"),
};
}