From 4c36475094bf2f74fad7dfb369871c115ac25e50 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 1 Jul 2026 11:57:54 +0000 Subject: [PATCH] fix(web-sentinel): configure apiserver endpoint for manual trigger --- config/hwlab-web-probe-sentinel/profiles.yaml | 10 ++++++++-- scripts/src/hwlab-node-web-sentinel-cicd.ts | 15 +++++++++++---- scripts/src/hwlab-node-web-sentinel-service.ts | 12 ++++++------ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/config/hwlab-web-probe-sentinel/profiles.yaml b/config/hwlab-web-probe-sentinel/profiles.yaml index d65c4d19..590bfa03 100644 --- a/config/hwlab-web-probe-sentinel/profiles.yaml +++ b/config/hwlab-web-probe-sentinel/profiles.yaml @@ -35,10 +35,16 @@ baselines: sampler: parentbased_traceidratio samplerArg: "1" kubernetesApi: + endpoint: + host: 172.16.0.5 + port: 6443 egress: enabled: true - cidr: 10.43.0.1/32 - port: 443 + rules: + - cidr: 10.43.0.1/32 + port: 443 + - cidr: 172.16.0.5/32 + port: 6443 scheduler15m: &scheduler-15m intervalMs: 900000 heartbeatStaleSeconds: 900 diff --git a/scripts/src/hwlab-node-web-sentinel-cicd.ts b/scripts/src/hwlab-node-web-sentinel-cicd.ts index 9241215d..3e1d7fc6 100644 --- a/scripts/src/hwlab-node-web-sentinel-cicd.ts +++ b/scripts/src/hwlab-node-web-sentinel-cicd.ts @@ -1248,10 +1248,12 @@ function renderSentinelManifests( function sentinelKubernetesApiEgress(runtime: Record): readonly Record[] { if (booleanAtNullable(runtime, "kubernetesApi.egress.enabled") !== true) return []; - return [{ - to: [{ ipBlock: { cidr: stringAt(runtime, "kubernetesApi.egress.cidr") } }], - ports: [{ protocol: "TCP", port: numberAt(runtime, "kubernetesApi.egress.port") }], - }]; + const rules = arrayAtNullable(runtime, "kubernetesApi.egress.rules"); + const rows = rules.length > 0 ? rules : [{ cidr: stringAt(runtime, "kubernetesApi.egress.cidr"), port: numberAt(runtime, "kubernetesApi.egress.port") }]; + return rows.map((rule) => ({ + to: [{ ipBlock: { cidr: stringAt(rule, "cidr") } }], + ports: [{ protocol: "TCP", port: numberAt(rule, "port") }], + })); } function sentinelContainerEnv(sentinelId: string, runtime: Record, cicd: Record, secrets: Record): readonly Record[] { @@ -4385,6 +4387,11 @@ export function arrayAt(value: unknown, path: string): unknown[] { return found; } +function arrayAtNullable(value: unknown, path: string): Record[] { + const found = valueAtPath(value, path); + return Array.isArray(found) ? found.map(record) : []; +} + export function recordTarget(value: unknown, label: string): Record { if (!isRecord(value)) throw new Error(`${label} must resolve to an object`); return value; diff --git a/scripts/src/hwlab-node-web-sentinel-service.ts b/scripts/src/hwlab-node-web-sentinel-service.ts index 83275910..189d41ba 100644 --- a/scripts/src/hwlab-node-web-sentinel-service.ts +++ b/scripts/src/hwlab-node-web-sentinel-service.ts @@ -2038,7 +2038,7 @@ async function triggerQuickVerifyCronJob(config: WebProbeSentinelServiceConfig, return { ok: false, status: "blocked", reason: "scenario-missing", namespace, cronJobName, valuesRedacted: true }; } - const activeJobs = await k8sApiJson("GET", `/apis/batch/v1/namespaces/${encodeURIComponent(namespace)}/jobs?labelSelector=${encodeURIComponent(`unidesk.ai/web-probe-sentinel-id=${config.sentinelId}`)}`, null); + const activeJobs = await k8sApiJson(config, "GET", `/apis/batch/v1/namespaces/${encodeURIComponent(namespace)}/jobs?labelSelector=${encodeURIComponent(`unidesk.ai/web-probe-sentinel-id=${config.sentinelId}`)}`, null); if (activeJobs.ok === true) { const active = k8sJobItems(activeJobs.bodyJson).filter((job) => numberAtNullable(job, "status.active") > 0); if (active.length > 0) { @@ -2058,7 +2058,7 @@ async function triggerQuickVerifyCronJob(config: WebProbeSentinelServiceConfig, } } - const cronJob = await k8sApiJson("GET", `/apis/batch/v1/namespaces/${encodeURIComponent(namespace)}/cronjobs/${encodeURIComponent(cronJobName)}`, null); + const cronJob = await k8sApiJson(config, "GET", `/apis/batch/v1/namespaces/${encodeURIComponent(namespace)}/cronjobs/${encodeURIComponent(cronJobName)}`, null); if (cronJob.ok !== true) { return { ok: false, @@ -2076,7 +2076,7 @@ async function triggerQuickVerifyCronJob(config: WebProbeSentinelServiceConfig, const now = nowIso(); const jobName = manualQuickVerifyJobName(config, now); const job = jobFromCronJobTemplate(config, record(cronJob.bodyJson), jobName, now, reason, source); - const created = await k8sApiJson("POST", `/apis/batch/v1/namespaces/${encodeURIComponent(namespace)}/jobs`, job); + const created = await k8sApiJson(config, "POST", `/apis/batch/v1/namespaces/${encodeURIComponent(namespace)}/jobs`, job); if (created.ok !== true) { return { ok: false, @@ -2145,9 +2145,9 @@ function jobFromCronJobTemplate( }; } -async function k8sApiJson(method: "GET" | "POST", path: string, body: Record | null): Promise> { - const host = process.env.KUBERNETES_SERVICE_HOST; - const port = Number(process.env.KUBERNETES_SERVICE_PORT_HTTPS ?? process.env.KUBERNETES_SERVICE_PORT ?? 443); +async function k8sApiJson(config: WebProbeSentinelServiceConfig, method: "GET" | "POST", path: string, body: Record | null): Promise> { + const host = stringAtNullable(config.runtime, "kubernetesApi.endpoint.host") ?? process.env.KUBERNETES_SERVICE_HOST; + const port = numberAtNullable(config.runtime, "kubernetesApi.endpoint.port") || Number(process.env.KUBERNETES_SERVICE_PORT_HTTPS ?? process.env.KUBERNETES_SERVICE_PORT ?? 443); if (typeof host !== "string" || host.length === 0) return { ok: false, status: "blocked", reason: "kubernetes-service-env-missing", valuesRedacted: true }; const tokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"; const caPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";