fix: protect jd01 host docker during remote gc
Pipelines as Code CI / hwlab-web-probe-sentinel-jd01- Failed

This commit is contained in:
Codex
2026-07-05 17:47:02 +00:00
parent 00b7878200
commit 91bee0832f
2 changed files with 42 additions and 4 deletions
+7
View File
@@ -109,6 +109,13 @@ gc:
remote:
targets:
JD01:
hostDockerGc:
dockerJsonLogs:
enabled: false
reason: JD01 host Docker stores the provider-gateway/trans runtime; do not truncate provider container logs from generic remote GC.
buildCachePrune:
enabled: false
reason: JD01 host Docker stores the provider-gateway/trans runtime; Docker builder prune previously preceded snapshot/lowerdir corruption.
memoryPressure:
processPatterns:
- chrome
+35 -4
View File
@@ -22,6 +22,7 @@ CONTAINERD_CONFIG = REMOTE_TARGET.get("containerdImageCache") if isinstance(REMO
HOST_CONTAINERD_CONFIG = REMOTE_TARGET.get("hostContainerdCache") if isinstance(REMOTE_TARGET.get("hostContainerdCache"), dict) else {}
LOCAL_PATH_CONFIG = REMOTE_TARGET.get("localPathStorage") if isinstance(REMOTE_TARGET.get("localPathStorage"), dict) else {}
POLICY_TIMER_CONFIG = REMOTE_TARGET.get("policyTimer") if isinstance(REMOTE_TARGET.get("policyTimer"), dict) else {}
HOST_DOCKER_GC_CONFIG = REMOTE_TARGET.get("hostDockerGc") if isinstance(REMOTE_TARGET.get("hostDockerGc"), dict) else {}
POLICY_RUNNER_SOURCE = base64.b64decode("__UNIDESK_GC_REMOTE_POLICY_RUNNER_BASE64__").decode("utf-8")
TMP_PREFIX_ALLOWLIST = [
@@ -142,6 +143,22 @@ def config_str(cfg, key, default=""):
return value
return str(default)
def host_docker_gc_section(name):
value = HOST_DOCKER_GC_CONFIG.get(name) if isinstance(HOST_DOCKER_GC_CONFIG, dict) else None
return value if isinstance(value, dict) else {}
def host_docker_gc_enabled(name, default_enabled=True):
section = host_docker_gc_section(name)
if "enabled" in section:
return bool(section.get("enabled"))
if PROVIDER_ID.upper() == "JD01":
return False
return bool(default_enabled)
def host_docker_gc_reason(name, fallback):
reason = host_docker_gc_section(name).get("reason")
return str(reason) if isinstance(reason, str) and reason else fallback
def parse_size_value(value, default=None):
if isinstance(value, (int, float)) and value > 0:
return int(value)
@@ -374,11 +391,13 @@ def path_size(path):
except OSError:
return 0
def du_size(path, timeout=20):
def du_size(path, timeout=20, fallback_walk=True):
if not os.path.exists(path):
return None
result = command(["du", "-sxB1", path], timeout)
if result["exitCode"] != 0:
if not fallback_walk:
return None
return path_size(path)
text = result["stdout"].strip()
if not text:
@@ -386,6 +405,8 @@ def du_size(path, timeout=20):
try:
return int(text.split()[0])
except Exception:
if not fallback_walk:
return None
return path_size(path)
def safe_int(value, default=0):
@@ -931,11 +952,17 @@ def collect_protected():
("docker-images-and-volumes", "docker-images-volumes", "Remote gc does not remove Docker images, containers, volumes or Compose projects."),
("k8s-api-objects", "deployments-statefulsets-secrets-pvcs", "Remote gc does not mutate Kubernetes workloads, Secrets, PVCs, PVs, Argo CD or Tekton objects."),
]
if not host_docker_gc_enabled("dockerJsonLogs"):
protected_paths.append(("host-docker-json-logs", "/var/lib/docker/containers", host_docker_gc_reason("dockerJsonLogs", "Host Docker json logs are protected on this provider.")))
if not host_docker_gc_enabled("buildCachePrune"):
protected_paths.append(("host-docker-build-cache", "docker-builder-cache", host_docker_gc_reason("buildCachePrune", "Host Docker builder cache prune is disabled on this provider.")))
result = []
for kind, ref, reason in protected_paths:
item = {"kind": kind, "risk": "blocked", "ref": ref, "reason": reason}
if ref.startswith("/") and os.path.exists(ref):
item["sizeBytes"] = du_size(ref)
size = du_size(ref, 3, False)
item["sizeBytes"] = size
item["sizeState"] = "sampled" if size is not None else "timeout-or-unavailable"
result.append(item)
return result
@@ -956,7 +983,7 @@ def collect_candidates(observed_at):
"action": {"command": ["journalctl", "--vacuum-size=%s" % target]},
})
if OPTIONS.get("dockerLogs", True):
if OPTIONS.get("dockerLogs", True) and host_docker_gc_enabled("dockerJsonLogs"):
max_bytes = int(OPTIONS.get("dockerLogMaxBytes") or 52428800)
for container in docker_containers():
path = container.get("logPath") or ""
@@ -980,7 +1007,7 @@ def collect_candidates(observed_at):
"action": {"op": "truncate", "targetBytes": 0},
})
if OPTIONS.get("buildCache", True):
if OPTIONS.get("buildCache", True) and host_docker_gc_enabled("buildCachePrune"):
system_df = command(["docker", "system", "df"], 8)
if system_df["exitCode"] == 0:
cache = parse_docker_build_cache(system_df["stdout"])
@@ -1334,6 +1361,8 @@ def execute(candidate):
raise RuntimeError((result["stderr"] or "journalctl vacuum failed").strip())
return {"reclaimedBytes": None, "commandOutput": bounded(result)}
if kind == "docker-json-log-truncate":
if not host_docker_gc_enabled("dockerJsonLogs"):
raise RuntimeError(host_docker_gc_reason("dockerJsonLogs", "refusing Docker json log truncation on this provider"))
path = candidate.get("path") or ""
if not path.startswith("/var/lib/docker/containers/"):
raise RuntimeError("refusing to truncate Docker log outside /var/lib/docker/containers")
@@ -1342,6 +1371,8 @@ def execute(candidate):
handle.truncate(0)
return {"reclaimedBytes": before}
if kind == "docker-build-cache-prune":
if not host_docker_gc_enabled("buildCachePrune"):
raise RuntimeError(host_docker_gc_reason("buildCachePrune", "refusing Docker builder prune on this provider"))
until = str(OPTIONS.get("buildCacheUntil") or "24h")
result = command(["docker", "builder", "prune", "--all", "--force", "--filter", "until=%s" % until], 45)
if result["exitCode"] != 0: