diff --git a/config/platform-infra/temporal.yaml b/config/platform-infra/temporal.yaml index 8d722e3a..de4d4890 100644 --- a/config/platform-infra/temporal.yaml +++ b/config/platform-infra/temporal.yaml @@ -47,6 +47,7 @@ runtime: frontendPort: 7233 dynamicConfigFilePath: /etc/temporal/config/dynamicconfig/docker.yaml enableServiceLinks: false + shareProcessNamespace: true ui: deploymentName: temporal-ui serviceName: temporal-ui diff --git a/scripts/src/platform-infra-temporal.ts b/scripts/src/platform-infra-temporal.ts index 76113cd6..66a5028c 100644 --- a/scripts/src/platform-infra-temporal.ts +++ b/scripts/src/platform-infra-temporal.ts @@ -80,6 +80,7 @@ export interface TemporalConfig { frontendPort: number; dynamicConfigFilePath: string; enableServiceLinks: boolean; + shareProcessNamespace: boolean; }; ui: { deploymentName: string; @@ -290,6 +291,7 @@ spec: app.kubernetes.io/part-of: temporal spec: enableServiceLinks: ${server.enableServiceLinks} + shareProcessNamespace: ${server.shareProcessNamespace} containers: - name: temporal image: ${temporal.images.server} @@ -498,19 +500,26 @@ ui_ready="$(kubectl -n ${target.namespace} get deploy ${ui.deploymentName} -o js endpoint="$(kubectl -n ${target.namespace} get endpoints ${server.serviceName} -o jsonpath='{.subsets[0].addresses[0].ip}' 2>/dev/null || true)" ui_endpoint="$(kubectl -n ${target.namespace} get endpoints ${ui.serviceName} -o jsonpath='{.subsets[0].addresses[0].ip}' 2>/dev/null || true)" secret="$(kubectl -n ${target.namespace} get secret ${temporal.database.secretName} -o name 2>/dev/null || true)" +process_table="$(kubectl -n ${target.namespace} exec deploy/${server.deploymentName} -- ps -eo pid,ppid,stat,comm,args 2>/dev/null || true)" +pid1_command="$(printf '%s\n' "$process_table" | awk 'NR > 1 && $1 == 1 { print $4; exit }')" +temporal_pid="$(printf '%s\n' "$process_table" | awk 'NR > 1 && $4 == "temporal-server" { print $1; exit }')" +zombie_count="$(printf '%s\n' "$process_table" | awk 'NR > 1 && $3 ~ /^Z/ { count += 1 } END { print count + 0 }')" logical_present=0 for logical in ${logicalArgs}; do if kubectl -n ${target.namespace} exec deploy/${server.deploymentName} -- temporal operator namespace describe --address ${server.serviceName}:${server.frontendPort} --namespace "$logical" >/dev/null 2>&1; then logical_present=$((logical_present + 1)) fi done -python3 - "$server_ready" "$ui_ready" "$endpoint" "$ui_endpoint" "$secret" "$logical_present" "${logicalNames.length}" "${validate}" <<'PY' +python3 - "$server_ready" "$ui_ready" "$endpoint" "$ui_endpoint" "$secret" "$logical_present" "${logicalNames.length}" "$pid1_command" "$temporal_pid" "$zombie_count" "${validate}" <<'PY' import json, sys server_ready, ui_ready, endpoint, ui_endpoint, secret = sys.argv[1:6] logical_present, logical_expected = int(sys.argv[6]), int(sys.argv[7]) -validate = sys.argv[8] == "true" +pid1_command, temporal_pid = sys.argv[8], sys.argv[9] +zombie_count = int(sys.argv[10]) +validate = sys.argv[11] == "true" ready = int(server_ready or 0) >= ${server.replicas} and int(ui_ready or 0) >= ${ui.replicas} -ok = ready and bool(endpoint) and bool(ui_endpoint) and bool(secret) and logical_present == logical_expected +process_reaping = bool(pid1_command) and pid1_command != "temporal-server" and int(temporal_pid or 0) > 1 and zombie_count == 0 +ok = ready and bool(endpoint) and bool(ui_endpoint) and bool(secret) and logical_present == logical_expected and process_reaping print(json.dumps({ "ok": ok, "mode": "validate" if validate else "status", @@ -519,6 +528,7 @@ print(json.dumps({ "ui": {"readyReplicas": int(ui_ready or 0), "endpointPresent": bool(ui_endpoint), "publicUrl": "${temporal.publicExposure.publicBaseUrl}"}, "database": {"mode": "nc01-host-native-postgresql", "host": "${temporal.database.host}", "secretPresent": bool(secret), "valuesPrinted": False}, "logicalNamespaces": {"expected": logical_expected, "present": logical_present, "names": ${JSON.stringify(logicalNames)}}, + "processReaping": {"ok": process_reaping, "pid1Command": pid1_command, "temporalPid": int(temporal_pid or 0), "zombieCount": zombie_count}, }, ensure_ascii=False)) sys.exit(0 if ok else 1) PY @@ -623,6 +633,7 @@ export function readTemporalConfig(): TemporalConfig { ...deploymentConfig(server, `${configLabel}.runtime.server`, "frontendPort"), dynamicConfigFilePath: stringField(server, "dynamicConfigFilePath", `${configLabel}.runtime.server`), enableServiceLinks: booleanField(server, "enableServiceLinks", `${configLabel}.runtime.server`), + shareProcessNamespace: booleanField(server, "shareProcessNamespace", `${configLabel}.runtime.server`), }, ui: { ...deploymentConfig(ui, `${configLabel}.runtime.ui`, "port"), @@ -696,6 +707,7 @@ function policyChecks(temporal: TemporalConfig, target: TemporalTarget, manifest { name: "native-postgres", ok: temporal.database.configRef === "config/platform-db/postgres-nc01.yaml", detail: temporal.database.configRef }, { name: "no-in-cluster-postgres", ok: !/kind: StatefulSet|PersistentVolumeClaim|image: .*postgres/iu.test(manifest), detail: "Temporal manifest contains no PostgreSQL workload or PVC." }, { name: "cluster-internal-grpc", ok: !/type: (?:NodePort|LoadBalancer)/u.test(manifest), detail: `${temporal.runtime.server.serviceName}.${target.namespace}.svc.cluster.local` }, + { name: "pid1-reaper", ok: temporal.runtime.server.shareProcessNamespace && /shareProcessNamespace: true/u.test(manifest), detail: `${configLabel}.runtime.server.shareProcessNamespace` }, { name: "public-ui-only", ok: temporal.publicExposure.publicBaseUrl === "https://temporal.hwpod.com", detail: temporal.publicExposure.publicBaseUrl }, { name: "unified-web-admin", ok: temporal.runtime.ui.auth.secretName === "temporal-web-admin", detail: "Web password is distributed from unified-admin-password.txt through the secrets CLI." }, ]; @@ -711,6 +723,7 @@ function summary(temporal: TemporalConfig, target: TemporalTarget): Record