- Add Workspace domain (entity, repository, service, handler, DTO) - Add multi-tenant K8s client with tenant binding and quota management - Add K8s diagnostics client (instance diagnostics) - Add authorization middleware (authz package) - Restructure frontend to feature-based architecture (features/) - Add User Management page in configuration - Add AccessDenied page and route guards - Refactor shared components (form inputs, layout, UI) - Update Tailwind config for new design system - Add comprehensive documentation (docs/, tasks/, plans) - Improve cluster service with better kubeconfig handling - Add tests for crypto, config, helm client, tenant binding
78 lines
3.3 KiB
Python
78 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
# Covers Harbor Helm chart defaults retrieval: authenticate as admin, locate an
|
|
# available registry, fetch charts/nginx:22.1.1 values-yaml, and assert the
|
|
# response is the top-level chart values.yaml rather than a dependency values file.
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from urllib.error import HTTPError, URLError
|
|
from urllib.parse import quote
|
|
from urllib.request import Request, urlopen
|
|
|
|
|
|
BASE_URL = os.environ.get("BASE_URL", "http://localhost:18081/api/v1").rstrip("/")
|
|
ADMIN_USER = os.environ.get("ADMIN_USER", os.environ.get("BOOTSTRAP_ADMIN_USER", "admin"))
|
|
ADMIN_PASS = os.environ.get("ADMIN_PASS", os.environ.get("BOOTSTRAP_ADMIN_PASS", ""))
|
|
|
|
|
|
def request(method: str, path: str, token: str | None = None, payload=None):
|
|
headers = {"Accept": "application/json"}
|
|
data = None
|
|
if payload is not None:
|
|
headers["Content-Type"] = "application/json"
|
|
data = json.dumps(payload).encode("utf-8")
|
|
if token:
|
|
headers["Authorization"] = f"Bearer {token}"
|
|
req = Request(BASE_URL + path, data=data, headers=headers, method=method)
|
|
try:
|
|
with urlopen(req, timeout=60) as res:
|
|
body = res.read().decode("utf-8", errors="replace")
|
|
return res.status, json.loads(body) if body else None, body
|
|
except HTTPError as exc:
|
|
body = exc.read().decode("utf-8", errors="replace")
|
|
try:
|
|
parsed = json.loads(body) if body else None
|
|
except json.JSONDecodeError:
|
|
parsed = None
|
|
return exc.code, parsed, body
|
|
except URLError as exc:
|
|
print(f"FAIL: cannot reach {BASE_URL}: {exc}", file=sys.stderr)
|
|
return 1, None, str(exc)
|
|
|
|
|
|
def main() -> int:
|
|
if not ADMIN_PASS:
|
|
print("SKIP: ADMIN_PASS or BOOTSTRAP_ADMIN_PASS is required")
|
|
return 77
|
|
status, auth, body = request("POST", "/auth/login", payload={"username": ADMIN_USER, "password": ADMIN_PASS})
|
|
if status != 200 or not isinstance(auth, dict):
|
|
print(f"FAIL: admin login failed HTTP {status}: {body[:300]}", file=sys.stderr)
|
|
return 1
|
|
token = auth["accessToken"]
|
|
status, registries, body = request("GET", "/registries", token)
|
|
if status != 200 or not isinstance(registries, list) or not registries:
|
|
print(f"SKIP: no registries available for values-yaml contract: HTTP {status}")
|
|
return 77
|
|
registry_id = registries[0]["id"]
|
|
repository = "charts/nginx"
|
|
reference = "22.1.1"
|
|
path = f"/registries/{registry_id}/repositories/{quote(repository, safe='')}/artifacts/{quote(reference, safe='')}/values-yaml"
|
|
status, response, body = request("GET", path, token)
|
|
if status != 200 or not isinstance(response, dict):
|
|
print(f"FAIL: values-yaml returned HTTP {status}: {body[:300]}", file=sys.stderr)
|
|
return 1
|
|
values_yaml = response.get("valuesYaml", "")
|
|
if len(values_yaml) < 10_000 or "global:" not in values_yaml or "image:" not in values_yaml or "service:" not in values_yaml:
|
|
print("FAIL: values-yaml did not look like the top-level nginx chart values.yaml", file=sys.stderr)
|
|
return 1
|
|
if "exampleValue: common-chart" in values_yaml[:1000]:
|
|
print("FAIL: values-yaml returned dependency chart values instead of top-level chart values", file=sys.stderr)
|
|
return 1
|
|
print("PASS: chart values-yaml API contract")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|