feat: add platform gitea install entry
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
#!/bin/sh
|
||||
set -u
|
||||
|
||||
tmp="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
|
||||
json_tail() {
|
||||
name="$1"
|
||||
limit="${2:-2000}"
|
||||
python3 - "$tmp/$name" "$limit" <<'PY'
|
||||
import json, sys
|
||||
path, limit = sys.argv[1], int(sys.argv[2])
|
||||
try:
|
||||
data = open(path, encoding="utf-8", errors="replace").read()[-limit:]
|
||||
except FileNotFoundError:
|
||||
data = ""
|
||||
print(json.dumps(data))
|
||||
PY
|
||||
}
|
||||
|
||||
capture_json() {
|
||||
name="$1"
|
||||
shift
|
||||
"$@" -o json >"$tmp/$name.json" 2>"$tmp/$name.err"
|
||||
rc=$?
|
||||
printf '%s' "$rc" >"$tmp/$name.rc"
|
||||
}
|
||||
|
||||
run_apply() {
|
||||
manifest="$tmp/gitea.k8s.yaml"
|
||||
printf '%s' "$UNIDESK_GITEA_MANIFEST_B64" | base64 -d >"$manifest"
|
||||
dry_arg=""
|
||||
if [ "$UNIDESK_GITEA_DRY_RUN" = "1" ]; then
|
||||
dry_arg="--dry-run=server"
|
||||
fi
|
||||
timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl apply --server-side $dry_arg --force-conflicts --field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f "$manifest" >"$tmp/apply.out" 2>"$tmp/apply.err"
|
||||
apply_rc=$?
|
||||
if [ "$apply_rc" -eq 0 ] && [ "$UNIDESK_GITEA_WAIT" = "1" ] && [ "$UNIDESK_GITEA_DRY_RUN" != "1" ]; then
|
||||
timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl -n "$UNIDESK_GITEA_NAMESPACE" rollout status "statefulset/$UNIDESK_GITEA_STATEFULSET_NAME" --timeout="${UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS}s" >"$tmp/rollout.out" 2>"$tmp/rollout.err"
|
||||
rollout_rc=$?
|
||||
else
|
||||
rollout_rc=0
|
||||
: >"$tmp/rollout.out"
|
||||
if [ "$UNIDESK_GITEA_DRY_RUN" = "1" ]; then
|
||||
printf '%s\n' "dry-run: rollout wait skipped" >"$tmp/rollout.err"
|
||||
else
|
||||
printf '%s\n' "rollout wait not requested" >"$tmp/rollout.err"
|
||||
fi
|
||||
fi
|
||||
python3 - "$apply_rc" "$rollout_rc" "$tmp/apply.out" "$tmp/apply.err" "$tmp/rollout.out" "$tmp/rollout.err" <<'PY'
|
||||
import json, os, sys
|
||||
apply_rc, rollout_rc = int(sys.argv[1]), int(sys.argv[2])
|
||||
def text(path, limit=3000):
|
||||
try:
|
||||
return open(path, encoding="utf-8", errors="replace").read()[-limit:]
|
||||
except FileNotFoundError:
|
||||
return ""
|
||||
dry = os.environ.get("UNIDESK_GITEA_DRY_RUN") == "1"
|
||||
payload = {
|
||||
"ok": apply_rc == 0 and rollout_rc == 0,
|
||||
"target": os.environ.get("UNIDESK_GITEA_TARGET_ID"),
|
||||
"route": os.environ.get("UNIDESK_GITEA_ROUTE"),
|
||||
"namespace": os.environ.get("UNIDESK_GITEA_NAMESPACE"),
|
||||
"mode": "dry-run" if dry else "confirmed",
|
||||
"mutation": not dry,
|
||||
"image": os.environ.get("UNIDESK_GITEA_IMAGE"),
|
||||
"objects": {
|
||||
"statefulSet": os.environ.get("UNIDESK_GITEA_STATEFULSET_NAME"),
|
||||
"service": os.environ.get("UNIDESK_GITEA_SERVICE_NAME"),
|
||||
"networkPolicy": "allow-all",
|
||||
},
|
||||
"steps": {
|
||||
"apply": {"exitCode": apply_rc, "stdoutTail": text(sys.argv[3]), "stderrTail": text(sys.argv[4])},
|
||||
"rollout": {"exitCode": rollout_rc, "stdoutTail": text(sys.argv[5]), "stderrTail": text(sys.argv[6])},
|
||||
},
|
||||
"valuesPrinted": False,
|
||||
}
|
||||
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
||||
sys.exit(0 if payload["ok"] else 1)
|
||||
PY
|
||||
}
|
||||
|
||||
run_status() {
|
||||
selector="app.kubernetes.io/name=$UNIDESK_GITEA_APP_NAME,app.kubernetes.io/component=gitea"
|
||||
capture_json namespace kubectl get namespace "$UNIDESK_GITEA_NAMESPACE"
|
||||
capture_json networkpolicy kubectl -n "$UNIDESK_GITEA_NAMESPACE" get networkpolicy allow-all
|
||||
capture_json statefulset kubectl -n "$UNIDESK_GITEA_NAMESPACE" get statefulset "$UNIDESK_GITEA_STATEFULSET_NAME"
|
||||
capture_json service kubectl -n "$UNIDESK_GITEA_NAMESPACE" get service "$UNIDESK_GITEA_SERVICE_NAME"
|
||||
capture_json endpoints kubectl -n "$UNIDESK_GITEA_NAMESPACE" get endpoints "$UNIDESK_GITEA_SERVICE_NAME"
|
||||
capture_json pods kubectl -n "$UNIDESK_GITEA_NAMESPACE" get pods -l "$selector"
|
||||
capture_json pvc kubectl -n "$UNIDESK_GITEA_NAMESPACE" get pvc -l "$selector"
|
||||
capture_json events kubectl -n "$UNIDESK_GITEA_NAMESPACE" get events --sort-by=.lastTimestamp
|
||||
python3 - "$tmp" <<'PY'
|
||||
import json, os, sys
|
||||
tmp = sys.argv[1]
|
||||
def rc(name):
|
||||
try:
|
||||
return int(open(f"{tmp}/{name}.rc", encoding="utf-8").read() or "1")
|
||||
except FileNotFoundError:
|
||||
return 1
|
||||
def load(name):
|
||||
try:
|
||||
return json.load(open(f"{tmp}/{name}.json", encoding="utf-8"))
|
||||
except Exception:
|
||||
return None
|
||||
def items(name):
|
||||
data = load(name)
|
||||
if isinstance(data, dict) and isinstance(data.get("items"), list):
|
||||
return data["items"]
|
||||
if isinstance(data, dict) and data.get("kind") != "Status":
|
||||
return [data]
|
||||
return []
|
||||
def meta_name(item):
|
||||
return ((item or {}).get("metadata") or {}).get("name")
|
||||
def pod_ready(item):
|
||||
status = (item or {}).get("status") or {}
|
||||
return any(c.get("type") == "Ready" and c.get("status") == "True" for c in status.get("conditions", []))
|
||||
def pod_summary(item):
|
||||
status = (item or {}).get("status") or {}
|
||||
containers = status.get("containerStatuses") or []
|
||||
return {
|
||||
"name": meta_name(item),
|
||||
"phase": status.get("phase"),
|
||||
"ready": pod_ready(item),
|
||||
"restarts": sum(int(c.get("restartCount") or 0) for c in containers if isinstance(c, dict)),
|
||||
}
|
||||
def service_summary(item):
|
||||
spec = (item or {}).get("spec") or {}
|
||||
return {
|
||||
"name": meta_name(item),
|
||||
"type": spec.get("type"),
|
||||
"clusterIP": spec.get("clusterIP"),
|
||||
"ports": [{"name": p.get("name"), "port": p.get("port"), "targetPort": p.get("targetPort")} for p in spec.get("ports", [])],
|
||||
}
|
||||
def endpoint_ready(item):
|
||||
subsets = ((item or {}).get("subsets") or [])
|
||||
return any(s.get("addresses") for s in subsets if isinstance(s, dict))
|
||||
def pvc_summary(item):
|
||||
spec = (item or {}).get("spec") or {}
|
||||
status = (item or {}).get("status") or {}
|
||||
return {
|
||||
"name": meta_name(item),
|
||||
"phase": status.get("phase"),
|
||||
"storageClassName": spec.get("storageClassName"),
|
||||
"capacity": (status.get("capacity") or {}).get("storage"),
|
||||
}
|
||||
def event_tail(limit=8):
|
||||
result = []
|
||||
for item in items("events")[-limit:]:
|
||||
result.append({
|
||||
"type": item.get("type"),
|
||||
"reason": item.get("reason"),
|
||||
"object": f"{((item.get('involvedObject') or {}).get('kind') or '-')}/{((item.get('involvedObject') or {}).get('name') or '-')}",
|
||||
"message": (item.get("message") or "")[:300],
|
||||
"lastTimestamp": item.get("lastTimestamp") or item.get("eventTime"),
|
||||
})
|
||||
return result
|
||||
sts = load("statefulset") or {}
|
||||
sts_spec = sts.get("spec") or {}
|
||||
sts_status = sts.get("status") or {}
|
||||
desired = int(sts_spec.get("replicas") or 0)
|
||||
ready_replicas = int(sts_status.get("readyReplicas") or 0)
|
||||
pods = [pod_summary(item) for item in items("pods")]
|
||||
service = service_summary(load("service") or {})
|
||||
endpoint_ok = endpoint_ready(load("endpoints") or {})
|
||||
ready = rc("namespace") == 0 and rc("networkpolicy") == 0 and rc("service") == 0 and desired > 0 and ready_replicas >= desired and endpoint_ok
|
||||
payload = {
|
||||
"ok": True,
|
||||
"ready": ready,
|
||||
"target": os.environ.get("UNIDESK_GITEA_TARGET_ID"),
|
||||
"route": os.environ.get("UNIDESK_GITEA_ROUTE"),
|
||||
"namespace": os.environ.get("UNIDESK_GITEA_NAMESPACE"),
|
||||
"image": os.environ.get("UNIDESK_GITEA_IMAGE"),
|
||||
"serviceDns": f"{os.environ.get('UNIDESK_GITEA_SERVICE_NAME')}.{os.environ.get('UNIDESK_GITEA_NAMESPACE')}.svc.cluster.local:{os.environ.get('UNIDESK_GITEA_HTTP_PORT')}",
|
||||
"networkPolicy": {"allowAllPresent": rc("networkpolicy") == 0},
|
||||
"statefulSet": {"name": meta_name(sts), "desired": desired, "readyReplicas": ready_replicas, "updatedReplicas": sts_status.get("updatedReplicas")},
|
||||
"service": service,
|
||||
"endpointsReady": endpoint_ok,
|
||||
"pods": pods,
|
||||
"pvcs": [pvc_summary(item) for item in items("pvc")],
|
||||
"eventsTail": event_tail(),
|
||||
"valuesPrinted": False,
|
||||
}
|
||||
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
||||
sys.exit(0)
|
||||
PY
|
||||
}
|
||||
|
||||
run_validate() {
|
||||
proxy_path="/api/v1/namespaces/$UNIDESK_GITEA_NAMESPACE/services/http:$UNIDESK_GITEA_SERVICE_NAME:$UNIDESK_GITEA_HTTP_PORT/proxy$UNIDESK_GITEA_HEALTH_PATH"
|
||||
kubectl get --raw "$proxy_path" >"$tmp/health.out" 2>"$tmp/health.err"
|
||||
health_rc=$?
|
||||
capture_json endpoints kubectl -n "$UNIDESK_GITEA_NAMESPACE" get endpoints "$UNIDESK_GITEA_SERVICE_NAME"
|
||||
python3 - "$health_rc" "$tmp/health.out" "$tmp/health.err" "$tmp/endpoints.json" "$tmp/endpoints.rc" <<'PY'
|
||||
import json, os, sys
|
||||
health_rc = int(sys.argv[1])
|
||||
def text(path, limit=2000):
|
||||
try:
|
||||
return open(path, encoding="utf-8", errors="replace").read()[-limit:]
|
||||
except FileNotFoundError:
|
||||
return ""
|
||||
body = text(sys.argv[2], 4000)
|
||||
try:
|
||||
parsed = json.loads(body)
|
||||
except Exception:
|
||||
parsed = None
|
||||
try:
|
||||
endpoint_rc = int(open(sys.argv[5], encoding="utf-8").read() or "1")
|
||||
except Exception:
|
||||
endpoint_rc = 1
|
||||
try:
|
||||
endpoints = json.load(open(sys.argv[4], encoding="utf-8"))
|
||||
except Exception:
|
||||
endpoints = {}
|
||||
subsets = endpoints.get("subsets") if isinstance(endpoints, dict) else []
|
||||
endpoints_ready = any(isinstance(s, dict) and s.get("addresses") for s in (subsets or []))
|
||||
health_status = parsed.get("status") if isinstance(parsed, dict) else None
|
||||
ok = health_rc == 0 and endpoint_rc == 0 and endpoints_ready and health_status == "pass"
|
||||
payload = {
|
||||
"ok": ok,
|
||||
"target": os.environ.get("UNIDESK_GITEA_TARGET_ID"),
|
||||
"route": os.environ.get("UNIDESK_GITEA_ROUTE"),
|
||||
"namespace": os.environ.get("UNIDESK_GITEA_NAMESPACE"),
|
||||
"service": os.environ.get("UNIDESK_GITEA_SERVICE_NAME"),
|
||||
"serviceProxyPath": f"/api/v1/namespaces/{os.environ.get('UNIDESK_GITEA_NAMESPACE')}/services/http:{os.environ.get('UNIDESK_GITEA_SERVICE_NAME')}:{os.environ.get('UNIDESK_GITEA_HTTP_PORT')}/proxy{os.environ.get('UNIDESK_GITEA_HEALTH_PATH')}",
|
||||
"health": {
|
||||
"exitCode": health_rc,
|
||||
"status": health_status,
|
||||
"description": parsed.get("description") if isinstance(parsed, dict) else None,
|
||||
"bodyBytes": len(body.encode("utf-8")),
|
||||
"stderrTail": text(sys.argv[3]),
|
||||
},
|
||||
"endpointsReady": endpoints_ready,
|
||||
"valuesPrinted": False,
|
||||
}
|
||||
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
||||
sys.exit(0 if ok else 1)
|
||||
PY
|
||||
}
|
||||
|
||||
case "$UNIDESK_GITEA_ACTION" in
|
||||
apply) run_apply ;;
|
||||
status) run_status ;;
|
||||
validate) run_validate ;;
|
||||
*) printf '{"ok":false,"error":"unsupported-gitea-remote-action"}\n'; exit 2 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user