fix: 修复 Host Docker GC 合并回归
This commit is contained in:
@@ -1,6 +1,50 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
|
def parse_docker_human_size(raw):
|
||||||
|
raw = str(raw).split("(")[0].strip()
|
||||||
|
match = re.match(r"^([0-9.]+)\s*([KMGT]?B)$", raw, re.I)
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
multiplier = {"B": 1, "KB": 1000, "MB": 1000**2, "GB": 1000**3, "TB": 1000**4}.get(match.group(2).upper(), 1)
|
||||||
|
return int(float(match.group(1)) * multiplier)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_docker_build_cache(text):
|
||||||
|
for line in text.splitlines():
|
||||||
|
if not line.startswith("Build Cache"):
|
||||||
|
continue
|
||||||
|
match = re.match(r"^Build Cache\s+\S+\s+\S+\s+(\S+)\s+(\S+)", line.strip())
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
size = parse_docker_human_size(match.group(1))
|
||||||
|
reclaim = parse_docker_human_size(match.group(2))
|
||||||
|
if size is None or reclaim is None:
|
||||||
|
return None
|
||||||
|
return {"sizeBytes": size, "reclaimableBytes": reclaim}
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def build_cache_only_payload(observed_at):
|
||||||
|
disk_before = df_snapshot()
|
||||||
|
section = host_docker_gc_section("buildCachePrune")
|
||||||
|
until = str(OPTIONS.get("buildCacheUntil") or section.get("until") or "24h")
|
||||||
|
if not host_docker_gc_enabled("buildCachePrune"):
|
||||||
|
return {"ok": False, "action": "gc remote %s" % ACTION, "providerId": PROVIDER_ID, "scope": "build-cache-only", "dryRun": ACTION == "plan", "mutation": False, "diagnosis": host_docker_gc_reason("buildCachePrune", "BuildKit cache GC is disabled by YAML")}
|
||||||
|
system_df = command(["docker", "system", "df"], 8)
|
||||||
|
if system_df["exitCode"] != 0:
|
||||||
|
return {"ok": False, "action": "gc remote %s" % ACTION, "providerId": PROVIDER_ID, "scope": "build-cache-only", "dryRun": ACTION == "plan", "mutation": False, "diagnosis": bounded(system_df)}
|
||||||
|
cache = parse_docker_build_cache(system_df["stdout"])
|
||||||
|
candidate = None
|
||||||
|
if cache is not None and cache["reclaimableBytes"] > 0:
|
||||||
|
candidate = {"id": "docker-builder:prune", "kind": "docker-build-cache-prune", "risk": "low", "sizeBytes": cache["sizeBytes"], "estimatedReclaimBytes": cache["reclaimableBytes"], "action": {"command": ["docker", "builder", "prune", "--all", "--force", "--filter", "until=%s" % until]}}
|
||||||
|
payload = {"ok": True, "action": "gc remote %s" % ACTION, "providerId": PROVIDER_ID, "scope": "build-cache-only", "configSource": "config/unidesk-cli.yaml#gc.remote.targets.%s.hostDockerGc.buildCachePrune" % PROVIDER_ID, "dryRun": ACTION == "plan", "mutation": ACTION == "run" and candidate is not None, "observedAt": observed_at, "diskBefore": disk_before, "until": until, "candidates": [candidate] if candidate else []}
|
||||||
|
if ACTION == "run" and candidate is not None:
|
||||||
|
payload["result"] = execute(candidate)
|
||||||
|
payload["diskAfter"] = df_snapshot()
|
||||||
|
return payload
|
||||||
|
|
||||||
|
|
||||||
def docker_containers():
|
def docker_containers():
|
||||||
ps = command(["docker", "ps", "-qa", "--no-trunc"], 5)
|
ps = command(["docker", "ps", "-qa", "--no-trunc"], 5)
|
||||||
if ps["exitCode"] != 0 or not ps["stdout"].strip():
|
if ps["exitCode"] != 0 or not ps["stdout"].strip():
|
||||||
|
|||||||
@@ -120,6 +120,10 @@ def run_json(args, timeout=45):
|
|||||||
}
|
}
|
||||||
if process.returncode != 0:
|
if process.returncode != 0:
|
||||||
return None, result
|
return None, result
|
||||||
|
try:
|
||||||
|
return json.loads(process.stdout or "{}"), result
|
||||||
|
except Exception:
|
||||||
|
return None, result
|
||||||
|
|
||||||
|
|
||||||
def command_lines(args, timeout=30, limit=10000):
|
def command_lines(args, timeout=30, limit=10000):
|
||||||
@@ -150,10 +154,6 @@ def root_available_bytes():
|
|||||||
return int(result["stdoutTail"].splitlines()[-1].strip())
|
return int(result["stdoutTail"].splitlines()[-1].strip())
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
try:
|
|
||||||
return json.loads(process.stdout or "{}"), result
|
|
||||||
except Exception:
|
|
||||||
return None, result
|
|
||||||
|
|
||||||
|
|
||||||
def write_state(config, payload):
|
def write_state(config, payload):
|
||||||
|
|||||||
@@ -1053,47 +1053,6 @@ def parse_journal_usage(text):
|
|||||||
mult = {"": 1, "K": 1024, "M": 1024**2, "G": 1024**3, "T": 1024**4}.get(m.group(2).upper(), 1)
|
mult = {"": 1, "K": 1024, "M": 1024**2, "G": 1024**3, "T": 1024**4}.get(m.group(2).upper(), 1)
|
||||||
return int(float(m.group(1)) * mult)
|
return int(float(m.group(1)) * mult)
|
||||||
|
|
||||||
def parse_docker_human_size(raw):
|
|
||||||
raw = str(raw).split("(")[0].strip()
|
|
||||||
m = re.match(r"^([0-9.]+)\s*([KMGT]?B)$", raw, re.I)
|
|
||||||
if not m:
|
|
||||||
return None
|
|
||||||
mult = {"B": 1, "KB": 1000, "MB": 1000**2, "GB": 1000**3, "TB": 1000**4}.get(m.group(2).upper(), 1)
|
|
||||||
return int(float(m.group(1)) * mult)
|
|
||||||
|
|
||||||
def parse_docker_build_cache(text):
|
|
||||||
for line in text.splitlines():
|
|
||||||
if not line.startswith("Build Cache"):
|
|
||||||
continue
|
|
||||||
match = re.match(r"^Build Cache\s+\S+\s+\S+\s+(\S+)\s+(\S+)", line.strip())
|
|
||||||
if not match:
|
|
||||||
continue
|
|
||||||
size = parse_docker_human_size(match.group(1))
|
|
||||||
reclaim = parse_docker_human_size(match.group(2))
|
|
||||||
if size is None or reclaim is None:
|
|
||||||
return None
|
|
||||||
return {"sizeBytes": size, "reclaimableBytes": reclaim}
|
|
||||||
return None
|
|
||||||
|
|
||||||
def build_cache_only_payload(observed_at):
|
|
||||||
disk_before = df_snapshot()
|
|
||||||
section = host_docker_gc_section("buildCachePrune")
|
|
||||||
until = str(OPTIONS.get("buildCacheUntil") or section.get("until") or "24h")
|
|
||||||
if not host_docker_gc_enabled("buildCachePrune"):
|
|
||||||
return {"ok": False, "action": "gc remote %s" % ACTION, "providerId": PROVIDER_ID, "scope": "build-cache-only", "dryRun": ACTION == "plan", "mutation": False, "diagnosis": host_docker_gc_reason("buildCachePrune", "BuildKit cache GC is disabled by YAML")}
|
|
||||||
system_df = command(["docker", "system", "df"], 8)
|
|
||||||
if system_df["exitCode"] != 0:
|
|
||||||
return {"ok": False, "action": "gc remote %s" % ACTION, "providerId": PROVIDER_ID, "scope": "build-cache-only", "dryRun": ACTION == "plan", "mutation": False, "diagnosis": bounded(system_df)}
|
|
||||||
cache = parse_docker_build_cache(system_df["stdout"])
|
|
||||||
candidate = None
|
|
||||||
if cache is not None and cache["reclaimableBytes"] > 0:
|
|
||||||
candidate = {"id": "docker-builder:prune", "kind": "docker-build-cache-prune", "risk": "low", "sizeBytes": cache["sizeBytes"], "estimatedReclaimBytes": cache["reclaimableBytes"], "action": {"command": ["docker", "builder", "prune", "--all", "--force", "--filter", "until=%s" % until]}}
|
|
||||||
payload = {"ok": True, "action": "gc remote %s" % ACTION, "providerId": PROVIDER_ID, "scope": "build-cache-only", "configSource": "config/unidesk-cli.yaml#gc.remote.targets.%s.hostDockerGc.buildCachePrune" % PROVIDER_ID, "dryRun": ACTION == "plan", "mutation": ACTION == "run" and candidate is not None, "observedAt": observed_at, "diskBefore": disk_before, "until": until, "candidates": [candidate] if candidate else []}
|
|
||||||
if ACTION == "run" and candidate is not None:
|
|
||||||
payload["result"] = execute(candidate)
|
|
||||||
payload["diskAfter"] = df_snapshot()
|
|
||||||
return payload
|
|
||||||
|
|
||||||
# __UNIDESK_GC_REMOTE_HOST_DOCKER_HELPERS__
|
# __UNIDESK_GC_REMOTE_HOST_DOCKER_HELPERS__
|
||||||
|
|
||||||
def cluster_preflight():
|
def cluster_preflight():
|
||||||
|
|||||||
Reference in New Issue
Block a user