diff --git a/config/hwlab-node-control-plane.yaml b/config/hwlab-node-control-plane.yaml index d7b51a92..f9376e2f 100644 --- a/config/hwlab-node-control-plane.yaml +++ b/config/hwlab-node-control-plane.yaml @@ -287,6 +287,7 @@ nodes: listenHost: 127.0.0.1 listenPort: 5000 hostNetwork: true + deleteEnabled: true egressProxy: mode: host-route clientName: jd01-host-proxy @@ -396,6 +397,7 @@ nodes: listenHost: 0.0.0.0 listenPort: 5000 hostNetwork: true + deleteEnabled: true egressProxy: mode: host-route clientName: nc01-host-proxy diff --git a/docs/reference/gc.md b/docs/reference/gc.md index 09b6dba4..1e56a7dc 100644 --- a/docs/reference/gc.md +++ b/docs/reference/gc.md @@ -45,6 +45,7 @@ - `gc remote plan --include-registry`: - 从 `config/unidesk-cli.yaml#gc.remote.targets..registryRetention` 读取留存策略; - 通过显式 `configRef` 读取 node-local registry 的 endpoint、Deployment、namespace 和 PVC; + - registry 的 manifest 删除能力必须由同一 node YAML 的 `deleteEnabled` 声明并已渲染到运行 Deployment;缺失或漂移时 plan/run fail closed; - 保护当前 workload tag/digest refs、近期 tag、每仓库最新 tag、保护仓库和 digest closure; - 输出候选 manifest、保护原因、估算收益和 typed diagnosis; - 不支持的目标、PVC 未绑定、存储布局不可读或 workload 形状漂移时必须 fail closed。 diff --git a/scripts/src/gc-remote-registry.py b/scripts/src/gc-remote-registry.py index 60768e81..34ae03bc 100644 --- a/scripts/src/gc-remote-registry.py +++ b/scripts/src/gc-remote-registry.py @@ -514,7 +514,10 @@ def registry_deployment_preflight(): image = str(registry_container.get("image") or "") expected_image = str(identity.get("image") or "") image_matches = image.startswith("registry:") if PROVIDER_ID.upper() == "G14" and not REGISTRY_CONFIG else image == expected_image - ok = bool(has_storage and has_mount and image_matches and spec.get("hostNetwork") is True) + env = {str(item.get("name") or ""): str(item.get("value") or "") for item in registry_container.get("env") or []} + delete_enabled = env.get("REGISTRY_STORAGE_DELETE_ENABLED", "").lower() == "true" + expected_delete_enabled = bool(registry_spec().get("deleteEnabled")) + ok = bool(has_storage and has_mount and image_matches and spec.get("hostNetwork") is True and delete_enabled == expected_delete_enabled and delete_enabled) return { "ok": ok, "reason": "ok" if ok else "unexpected-registry-deployment-shape", @@ -527,6 +530,8 @@ def registry_deployment_preflight(): "hostNetwork": spec.get("hostNetwork"), "hasExpectedStorage": has_storage, "hasExpectedMount": has_mount, + "deleteEnabled": delete_enabled, + "expectedDeleteEnabled": expected_delete_enabled, "replicas": (dep.get("spec") or {}).get("replicas"), "readyReplicas": (dep.get("status") or {}).get("readyReplicas"), } diff --git a/scripts/src/hwlab-node-control-plane-model.ts b/scripts/src/hwlab-node-control-plane-model.ts index 3e526743..c5ab0652 100644 --- a/scripts/src/hwlab-node-control-plane-model.ts +++ b/scripts/src/hwlab-node-control-plane-model.ts @@ -234,6 +234,7 @@ export interface ControlPlaneK8sWorkloadRegistrySpec { listenHost: string; listenPort: number; hostNetwork: boolean; + deleteEnabled: boolean; } export interface ControlPlaneK3sNodeSpec { diff --git a/scripts/src/hwlab-node-control-plane-registry.ts b/scripts/src/hwlab-node-control-plane-registry.ts index c87da41d..92cc976b 100644 --- a/scripts/src/hwlab-node-control-plane-registry.ts +++ b/scripts/src/hwlab-node-control-plane-registry.ts @@ -21,6 +21,7 @@ export function controlPlaneRegistrySummary(registry: ControlPlaneRegistrySpec): listenHost: registry.listenHost, listenPort: registry.listenPort, hostNetwork: registry.hostNetwork, + deleteEnabled: registry.deleteEnabled, }; } @@ -32,7 +33,10 @@ export function registryInfraManifest(registry: ControlPlaneRegistrySpec, labels name: "registry", image: registry.image, imagePullPolicy: registry.imagePullPolicy, - env: [{ name: "REGISTRY_HTTP_ADDR", value: `${registry.listenHost}:${registry.listenPort}` }], + env: [ + { name: "REGISTRY_HTTP_ADDR", value: `${registry.listenHost}:${registry.listenPort}` }, + { name: "REGISTRY_STORAGE_DELETE_ENABLED", value: String(registry.deleteEnabled) }, + ], ports: [{ name: "http", containerPort: registry.containerPort }], volumeMounts: [{ name: "storage", mountPath: "/var/lib/registry" }], readinessProbe: { httpGet: { path: "/v2/", port: "http", host: registry.listenHost }, initialDelaySeconds: 3, periodSeconds: 10 }, diff --git a/scripts/src/hwlab-node-control-plane.ts b/scripts/src/hwlab-node-control-plane.ts index 94ad3681..8436f35f 100644 --- a/scripts/src/hwlab-node-control-plane.ts +++ b/scripts/src/hwlab-node-control-plane.ts @@ -1649,6 +1649,7 @@ function registrySpec(raw: Record, path: string): ControlPlaneR listenHost, listenPort: numberField(raw, "listenPort", path), hostNetwork, + deleteEnabled: booleanField(raw, "deleteEnabled", path), }; }