fix: scope codex-pool sentinel image to enabled targets
This commit is contained in:
@@ -95,9 +95,9 @@ export async function codexPoolSync(config: UniDeskConfig, options: SyncOptions)
|
||||
};
|
||||
}
|
||||
|
||||
const sentinelImage = pool.sentinel.monitor.enabled
|
||||
const sentinelImage = pool.sentinel.monitor.enabled && runtimeTarget.sentinelEnabled
|
||||
? await runCodexPoolSentinelImage(config, pool, { action: "build", confirm: true, dryRun: false, full: options.full, raw: false, targetId: options.targetId })
|
||||
: { ok: true, mode: "skipped-monitor-disabled" };
|
||||
: { ok: true, mode: runtimeTarget.sentinelEnabled ? "skipped-monitor-disabled" : "skipped-target-disabled" };
|
||||
if (sentinelImage.ok !== true) {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -113,17 +113,20 @@ export async function codexPoolSync(config: UniDeskConfig, options: SyncOptions)
|
||||
const payload = {
|
||||
pruneRemoved: options.pruneRemoved,
|
||||
sentinel: {
|
||||
manifest: renderCodexPoolSentinelManifest(pool.sentinel, sentinelProfileSecrets(profiles), {
|
||||
namespace: runtimeTarget.namespace,
|
||||
serviceName: runtimeTarget.serviceName,
|
||||
serviceDns: runtimeTarget.serviceDns,
|
||||
appSecretName: runtimeTarget.appSecretName,
|
||||
adminEmailDefault: pool.adminEmailDefault,
|
||||
proxy: runtimeTarget.egressProxy?.applyToSentinel ? {
|
||||
httpProxy: runtimeTarget.egressProxy.httpProxy,
|
||||
noProxy: runtimeTarget.egressProxy.noProxy,
|
||||
} : null,
|
||||
}),
|
||||
enabledForTarget: runtimeTarget.sentinelEnabled,
|
||||
manifest: runtimeTarget.sentinelEnabled
|
||||
? renderCodexPoolSentinelManifest(pool.sentinel, sentinelProfileSecrets(profiles), {
|
||||
namespace: runtimeTarget.namespace,
|
||||
serviceName: runtimeTarget.serviceName,
|
||||
serviceDns: runtimeTarget.serviceDns,
|
||||
appSecretName: runtimeTarget.appSecretName,
|
||||
adminEmailDefault: pool.adminEmailDefault,
|
||||
proxy: runtimeTarget.egressProxy?.applyToSentinel ? {
|
||||
httpProxy: runtimeTarget.egressProxy.httpProxy,
|
||||
noProxy: runtimeTarget.egressProxy.noProxy,
|
||||
} : null,
|
||||
})
|
||||
: null,
|
||||
summary: codexPoolSentinelSummary(pool.sentinel),
|
||||
},
|
||||
pool: {
|
||||
@@ -215,6 +218,20 @@ export async function codexPoolSentinelImage(config: UniDeskConfig, options: Sen
|
||||
export async function runCodexPoolSentinelImage(config: UniDeskConfig, pool: CodexPoolConfig, options: SentinelImageOptions): Promise<Record<string, unknown>> {
|
||||
const runtimeTarget = codexPoolRuntimeTarget(options.targetId);
|
||||
const target = codexPoolSentinelRuntimeImage(pool.sentinel);
|
||||
if (!runtimeTarget.sentinelEnabled) {
|
||||
return {
|
||||
ok: true,
|
||||
action: "platform-infra-sub2api-codex-pool-sentinel-image",
|
||||
mode: "skipped-target-disabled",
|
||||
target: {
|
||||
id: runtimeTarget.id,
|
||||
namespace: runtimeTarget.namespace,
|
||||
},
|
||||
image: target,
|
||||
mutation: false,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
if (options.action === "build" && options.dryRun) {
|
||||
return {
|
||||
ok: true,
|
||||
|
||||
@@ -324,5 +324,6 @@ export interface Sub2ApiRuntimeConfig {
|
||||
defaultTargetId: string;
|
||||
appSecretName: string;
|
||||
secretsRoot: string;
|
||||
sentinelEnabledOnTargets: string[];
|
||||
targets: Record<string, unknown>[];
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE = json.loads(${JSON.stringify(JSON.stringify
|
||||
MANUAL_ACCOUNT_PROTECTIONS = json.loads(${JSON.stringify(JSON.stringify(resolvedManualAccountProtections(pool, target)))})
|
||||
SENTINEL_CONFIG = json.loads(${JSON.stringify(JSON.stringify(pool.sentinel))})
|
||||
TARGET_EGRESS_PROXY = json.loads(${JSON.stringify(JSON.stringify(target.egressProxy))})
|
||||
TARGET_SENTINEL_ENABLED = ${JSON.stringify(target.sentinelEnabled)}
|
||||
MODE = "${mode}"
|
||||
PAYLOAD_B64 = "${encodedPayload}"
|
||||
|
||||
@@ -166,7 +167,7 @@ def empty_to_none(value):
|
||||
return value if isinstance(value, str) and value else None
|
||||
|
||||
def sentinel_quality_gate_enabled():
|
||||
return (SENTINEL_CONFIG.get("monitor") or {}).get("enabled") is True and (SENTINEL_CONFIG.get("actions") or {}).get("enabled") is True
|
||||
return TARGET_SENTINEL_ENABLED and (SENTINEL_CONFIG.get("monitor") or {}).get("enabled") is True and (SENTINEL_CONFIG.get("actions") or {}).get("enabled") is True
|
||||
|
||||
def account_notes_fingerprint(account):
|
||||
notes = account.get("notes") if isinstance(account, dict) else None
|
||||
@@ -981,6 +982,12 @@ def ensure_api_key_secret(group_id):
|
||||
return api_key, secret_action, text(proc.stdout, 1000)
|
||||
|
||||
def apply_sentinel_manifest(manifest):
|
||||
if not TARGET_SENTINEL_ENABLED:
|
||||
return {
|
||||
"ok": True,
|
||||
"action": "skipped-target-disabled",
|
||||
"valuesPrinted": False,
|
||||
}
|
||||
if not isinstance(manifest, str) or not manifest.strip():
|
||||
return {
|
||||
"ok": False,
|
||||
@@ -1433,6 +1440,8 @@ def sentinel_state_summary():
|
||||
}
|
||||
|
||||
def reassert_sentinel_freezes_after_sync(token):
|
||||
if not TARGET_SENTINEL_ENABLED:
|
||||
return {"ok": True, "skipped": True, "reason": "target-disabled", "items": [], "valuesPrinted": False}
|
||||
if (SENTINEL_CONFIG.get("actions") or {}).get("enabled") is not True:
|
||||
return {"ok": True, "skipped": True, "reason": "actions-disabled", "items": [], "valuesPrinted": False}
|
||||
_, state = sentinel_state_object()
|
||||
|
||||
@@ -41,11 +41,16 @@ export function readSub2ApiRuntimeConfig(): Sub2ApiRuntimeConfig {
|
||||
const secrets = runtime !== null && isRecord(runtime.secrets) ? runtime.secrets : null;
|
||||
const secretsRoot = secrets === null ? null : stringValue(secrets.root);
|
||||
if (secretsRoot === null || !secretsRoot.startsWith("/")) throw new Error(`${sub2apiConfigPath}.runtime.secrets.root must be an absolute path`);
|
||||
const sentinel = runtime !== null && isRecord(runtime.sentinel) ? runtime.sentinel : null;
|
||||
const enabledOnTargets = Array.isArray(sentinel?.enabledOnTargets)
|
||||
? sentinel.enabledOnTargets.map((entry) => stringValue(entry)).filter((entry): entry is string => entry !== null && entry.length > 0)
|
||||
: [];
|
||||
if (!Array.isArray(parsed.targets) || !parsed.targets.every(isRecord)) throw new Error(`${sub2apiConfigPath}.targets must be a list`);
|
||||
return {
|
||||
defaultTargetId,
|
||||
appSecretName,
|
||||
secretsRoot,
|
||||
sentinelEnabledOnTargets: enabledOnTargets,
|
||||
targets: parsed.targets,
|
||||
};
|
||||
}
|
||||
@@ -100,6 +105,7 @@ export function codexPoolRuntimeTarget(targetId?: string): CodexPoolRuntimeTarge
|
||||
publicExposure,
|
||||
appSecretName: runtimeConfig.appSecretName,
|
||||
secretsRoot: runtimeConfig.secretsRoot,
|
||||
sentinelEnabled: runtimeConfig.sentinelEnabledOnTargets.some((entry) => entry.toLowerCase() === id.toLowerCase()),
|
||||
sentinelImageBuild,
|
||||
egressProxy,
|
||||
};
|
||||
|
||||
@@ -87,6 +87,7 @@ export interface CodexPoolRuntimeTarget {
|
||||
publicExposure: PublicServiceExposure | null;
|
||||
appSecretName: string;
|
||||
secretsRoot: string;
|
||||
sentinelEnabled: boolean;
|
||||
sentinelImageBuild: {
|
||||
baseImageCachePolicy: "pull" | "local-if-present";
|
||||
noProxy: string;
|
||||
|
||||
Reference in New Issue
Block a user