prole/tests/etc/test_argocd_repo_server_hostpath_permissions.sh

127 lines
4.3 KiB
Bash

#!/usr/bin/env bash
# Regression test: ArgoCD repo-server must be able to write to /var/run/argocd when
# the manifest uses hostPath-backed volumes (common on k3d nodes).
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
PROLE_HOME=$(cd "$SCRIPT_DIR/../.." && pwd)
MANIFEST="$PROLE_HOME/k8s/argocd/install.yaml"
python3 - "$MANIFEST" <<'PY'
import sys
# NOTE: Avoid a dependency on PyYAML; validate via text scanning and block extraction.
import re
path = sys.argv[1]
text = open(path, "r", encoding="utf-8").read()
docs = []
buf = []
for line in text.splitlines(True):
if line.strip() == "---":
docs.append("".join(buf))
buf = []
continue
buf.append(line)
docs.append("".join(buf))
def is_target(doc: str) -> bool:
return (
re.search(r"(?m)^kind:\s*Deployment\s*$", doc) is not None
and re.search(r"(?m)^\s*name:\s*argocd-repo-server\s*$", doc) is not None
)
targets = [d for d in docs if is_target(d)]
assert len(targets) == 1, f"expected exactly 1 argocd-repo-server deployment doc, got {len(targets)}"
doc = targets[0]
def extract_block_after_key(text: str, key: str) -> tuple[int, str]:
m = re.search(rf"(?m)^(?P<indent>\s*){re.escape(key)}:\s*$", text)
assert m, f"missing key '{key}:'"
base_indent = len(m.group("indent"))
lines = text[m.end() :].splitlines(True)
out = []
for ln in lines:
if not ln.strip():
out.append(ln)
continue
ind = len(ln) - len(ln.lstrip(" "))
# Block ends when we return to a sibling key (same indent, not a list item) or higher.
if ind < base_indent:
break
if ind == base_indent and not ln.lstrip().startswith("-"):
break
out.append(ln)
return base_indent, "".join(out)
def split_list_items(block: str, item_indent: int) -> list[str]:
items: list[list[str]] = []
current: list[str] = []
for ln in block.splitlines(True):
if not ln.strip():
if current:
current.append(ln)
continue
ind = len(ln) - len(ln.lstrip(" "))
if ind == item_indent and ln.lstrip().startswith("-"):
if current:
items.append(current)
current = [ln]
else:
if current:
current.append(ln)
if current:
items.append(current)
return ["".join(i) for i in items]
def find_list_item_with_name(items: list[str], name: str) -> str:
for it in items:
if re.search(rf"(?m)^\s*name:\s*{re.escape(name)}\s*$", it):
return it
raise AssertionError(f"missing initContainer '{name}'")
ic_indent, ic_block = extract_block_after_key(doc, "initContainers")
ic_items = split_list_items(ic_block, ic_indent)
init_perm = find_list_item_with_name(ic_items, "init-permissions")
copyutil = find_list_item_with_name(ic_items, "copyutil")
assert re.search(r"(?m)^\s*runAsUser:\s*0\s*$", init_perm), "init-permissions must run as root (runAsUser: 0)"
assert "chown" in init_perm, "init-permissions should attempt chown when possible"
assert "chown failed" in init_perm, "init-permissions must tolerate chown failures"
assert "chmod 0777" in init_perm, "init-permissions must apply permissive chmod when chown is not allowed"
def list_item_has_mount(container_item: str, vol_name: str, mount_path: str) -> bool:
vm_indent, vm_block = extract_block_after_key(container_item, "volumeMounts")
vm_items = split_list_items(vm_block, vm_indent)
for it in vm_items:
if re.search(rf"(?m)^\s*name:\s*{re.escape(vol_name)}\s*$", it) and re.search(
rf"(?m)^\s*(?:-\s*)?mountPath:\s*{re.escape(mount_path)}\s*$", it
):
return True
return False
required = [
("var-files", "/var/run/argocd"),
("tmp", "/tmp"),
("helm-working-dir", "/helm-working-dir"),
("plugins", "/home/argocd/cmp-server/plugins"),
("gpg-keyring", "/app/config/gpg/keys"),
]
missing = [(n, p) for (n, p) in required if not list_item_has_mount(init_perm, n, p)]
assert not missing, f"init-permissions missing required mounts (name, mountPath): {missing}"
assert list_item_has_mount(copyutil, "var-files", "/var/run/argocd"), "copyutil must mount var-files at /var/run/argocd"
assert "/var/run/argocd/argocd" in copyutil, "copyutil must copy argocd binary into /var/run/argocd"
print("OK")
PY
echo "SUCCESS"