270 lines
8.7 KiB
Bash
Executable File
270 lines
8.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
run_id=""
|
|
repo_url="https://github.com/pikasTech/unidesk"
|
|
desired_ref="master"
|
|
manifest_commit=""
|
|
environment="dev"
|
|
result_dir=""
|
|
timeout_ms="1800000"
|
|
keep_namespace="false"
|
|
manifest_file=""
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
dev-e2e.sh --run-id ID --manifest-commit COMMIT --manifest-file FILE --result-dir DIR [--repo-url URL] [--desired-ref master] [--environment dev] [--timeout-ms MS] [--keep-namespace]
|
|
|
|
This script runs the D601 dev namespace e2e harness from a Git-controlled blob.
|
|
It must be launched by the CLI with a short command; do not paste this script
|
|
body through the maintenance channel.
|
|
EOF
|
|
}
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--run-id)
|
|
run_id="${2:-}"
|
|
shift 2
|
|
;;
|
|
--repo-url)
|
|
repo_url="${2:-}"
|
|
shift 2
|
|
;;
|
|
--desired-ref)
|
|
desired_ref="${2:-}"
|
|
shift 2
|
|
;;
|
|
--manifest-commit)
|
|
manifest_commit="${2:-}"
|
|
shift 2
|
|
;;
|
|
--environment)
|
|
environment="${2:-}"
|
|
shift 2
|
|
;;
|
|
--result-dir)
|
|
result_dir="${2:-}"
|
|
shift 2
|
|
;;
|
|
--manifest-file)
|
|
manifest_file="${2:-}"
|
|
shift 2
|
|
;;
|
|
--timeout-ms)
|
|
timeout_ms="${2:-}"
|
|
shift 2
|
|
;;
|
|
--keep-namespace)
|
|
keep_namespace="true"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "unknown argument: $1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if ! [[ "$run_id" =~ ^[a-z0-9]([-a-z0-9]{0,46}[a-z0-9])?$ ]]; then
|
|
echo "invalid --run-id: $run_id" >&2
|
|
exit 2
|
|
fi
|
|
if ! [[ "$manifest_commit" =~ ^[0-9a-f]{40}$ ]]; then
|
|
echo "--manifest-commit must be a full 40 character SHA" >&2
|
|
exit 2
|
|
fi
|
|
if [ "$environment" != "dev" ]; then
|
|
echo "only --environment dev is supported" >&2
|
|
exit 2
|
|
fi
|
|
if ! [[ "$timeout_ms" =~ ^[0-9]+$ ]] || [ "$timeout_ms" -le 0 ]; then
|
|
echo "--timeout-ms must be a positive integer" >&2
|
|
exit 2
|
|
fi
|
|
if [ -z "$result_dir" ]; then
|
|
result_dir="/home/ubuntu/.unidesk/runs/$run_id"
|
|
fi
|
|
if [ -z "$manifest_file" ] || [ ! -f "$manifest_file" ]; then
|
|
echo "--manifest-file must point to the commit-pinned deploy.json fetched by the launcher" >&2
|
|
exit 2
|
|
fi
|
|
|
|
mkdir -p "$result_dir"
|
|
runner_log="$result_dir/runner.log"
|
|
result_json="$result_dir/result.json"
|
|
exec > >(tee -a "$runner_log") 2>&1
|
|
|
|
log_json() {
|
|
local event="$1"
|
|
shift || true
|
|
printf '{"at":"%s","event":"%s"' "$(date -Iseconds)" "$event"
|
|
while [ "$#" -gt 1 ]; do
|
|
printf ',"%s":%s' "$1" "$(printf '%s' "$2" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')"
|
|
shift 2
|
|
done
|
|
printf '}\n'
|
|
}
|
|
|
|
write_result() {
|
|
local ok="$1"
|
|
local status="$2"
|
|
local detail="$3"
|
|
python3 - "$result_json" "$ok" "$status" "$detail" "$run_id" "$repo_url" "$desired_ref" "$manifest_commit" "$environment" "$pipeline_run" "$temporary_namespace" <<'PY'
|
|
import json
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
|
|
path, ok, status, detail, run_id, repo, desired_ref, commit, environment, pipeline_run, temporary_namespace = sys.argv[1:]
|
|
record = {
|
|
"ok": ok == "true",
|
|
"status": status,
|
|
"detail": detail,
|
|
"runId": run_id,
|
|
"repoUrl": repo,
|
|
"desiredRef": desired_ref,
|
|
"manifestCommit": commit,
|
|
"environment": environment,
|
|
"pipelineRun": pipeline_run or None,
|
|
"temporaryNamespace": temporary_namespace or None,
|
|
"finishedAt": datetime.now(timezone.utc).isoformat(),
|
|
}
|
|
with open(path, "w", encoding="utf-8") as handle:
|
|
json.dump(record, handle, ensure_ascii=False, indent=2)
|
|
handle.write("\n")
|
|
print(json.dumps(record, ensure_ascii=False))
|
|
PY
|
|
}
|
|
|
|
pipeline_run=""
|
|
temporary_namespace="unidesk-ci-e2e-$run_id"
|
|
trap 'code=$?; if [ "$code" -ne 0 ] && [ ! -f "$result_json" ]; then write_result false failed "runner exited with code $code" || true; fi' EXIT
|
|
|
|
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
|
|
kubectl get nodes >/dev/null
|
|
|
|
log_json runner_started run_id "$run_id" manifest_commit "$manifest_commit"
|
|
kubectl get pipeline/unidesk-dev-namespace-e2e -n unidesk-ci >/dev/null
|
|
kubectl get pvc/unidesk-ci-cache -n unidesk-ci >/dev/null
|
|
|
|
service_env="$result_dir/dev-e2e-service-commits.env"
|
|
python3 - "$manifest_file" "$service_env" "$environment" <<'PY'
|
|
import json
|
|
import re
|
|
import sys
|
|
|
|
manifest_path, out_path, environment = sys.argv[1:]
|
|
with open(manifest_path, "r", encoding="utf-8") as handle:
|
|
manifest = json.load(handle)
|
|
if manifest.get("schemaVersion") != 2:
|
|
raise SystemExit("deploy.json must use schemaVersion=2")
|
|
env = manifest.get("environments", {}).get(environment)
|
|
if not isinstance(env, dict):
|
|
raise SystemExit(f"deploy.json must contain environments.{environment}")
|
|
services = env.get("services")
|
|
if not isinstance(services, list) or not services:
|
|
raise SystemExit(f"deploy.json environments.{environment}.services must contain services")
|
|
lines = []
|
|
summary = []
|
|
for service in services:
|
|
if not isinstance(service, dict) or not service.get("id") or not service.get("repo") or not service.get("commitId"):
|
|
raise SystemExit(f"each deploy.json environments.{environment} service must contain id, repo and commitId")
|
|
key = re.sub(r"[^A-Z0-9]", "_", str(service["id"]).upper())
|
|
commit = str(service["commitId"])
|
|
lines.append(f"{key}_COMMIT={commit}")
|
|
summary.append({"id": service["id"], "commitId": commit})
|
|
with open(out_path, "w", encoding="utf-8") as handle:
|
|
handle.write("\n".join(lines) + "\n")
|
|
print(json.dumps({"ok": True, "environment": environment, "services": summary}, ensure_ascii=False))
|
|
PY
|
|
# shellcheck disable=SC1090
|
|
source "$service_env"
|
|
backend_commit="${BACKEND_CORE_COMMIT:-unknown}"
|
|
frontend_commit="${FRONTEND_COMMIT:-unknown}"
|
|
code_queue_commit="${CODE_QUEUE_COMMIT:-unknown}"
|
|
deploy_json_b64="$(base64 -w0 "$manifest_file")"
|
|
|
|
pipeline_manifest="$result_dir/pipelinerun.yaml"
|
|
cat >"$pipeline_manifest" <<YAML
|
|
apiVersion: tekton.dev/v1
|
|
kind: PipelineRun
|
|
metadata:
|
|
generateName: unidesk-dev-e2e-$run_id-
|
|
namespace: unidesk-ci
|
|
labels:
|
|
app.kubernetes.io/name: unidesk-dev-namespace-e2e
|
|
app.kubernetes.io/part-of: unidesk
|
|
unidesk.ai/ci-kind: dev-namespace-e2e
|
|
unidesk.ai/deploy-ref: master-deploy-json-dev
|
|
unidesk.ai/deploy-commit: "$manifest_commit"
|
|
spec:
|
|
pipelineRef:
|
|
name: unidesk-dev-namespace-e2e
|
|
taskRunTemplate:
|
|
serviceAccountName: unidesk-ci-runner
|
|
params:
|
|
- name: repo-url
|
|
value: "$repo_url"
|
|
- name: desired-ref
|
|
value: "$desired_ref"
|
|
- name: deploy-commit
|
|
value: "$manifest_commit"
|
|
- name: environment
|
|
value: "$environment"
|
|
- name: run-id
|
|
value: "$run_id"
|
|
- name: keep-namespace
|
|
value: "$keep_namespace"
|
|
- name: backend-core-commit
|
|
value: "$backend_commit"
|
|
- name: frontend-commit
|
|
value: "$frontend_commit"
|
|
- name: code-queue-commit
|
|
value: "$code_queue_commit"
|
|
- name: deploy-json-b64
|
|
value: "$deploy_json_b64"
|
|
workspaces:
|
|
- name: shared-workspace
|
|
persistentVolumeClaim:
|
|
claimName: unidesk-ci-cache
|
|
YAML
|
|
|
|
pipeline_run="$(kubectl create -f "$pipeline_manifest" -o jsonpath='{.metadata.name}')"
|
|
printf '%s\n' "$pipeline_run" >"$result_dir/pipelinerun.txt"
|
|
log_json pipelinerun_created pipeline_run "$pipeline_run" namespace unidesk-ci
|
|
|
|
deadline=$((SECONDS + (timeout_ms + 999) / 1000))
|
|
condition=""
|
|
while [ "$SECONDS" -lt "$deadline" ]; do
|
|
condition="$(kubectl get "pipelinerun/$pipeline_run" -n unidesk-ci -o jsonpath='{range .status.conditions[?(@.type=="Succeeded")]}{.status}{"\t"}{.reason}{"\t"}{.message}{end}' 2>/dev/null || true)"
|
|
case "$condition" in
|
|
True*)
|
|
kubectl get "pipelinerun/$pipeline_run" -n unidesk-ci -o json >"$result_dir/pipelinerun.json"
|
|
kubectl get taskrun -n unidesk-ci -l "tekton.dev/pipelineRun=$pipeline_run" -o json >"$result_dir/taskruns.json" || true
|
|
kubectl logs -n unidesk-ci -l "tekton.dev/pipelineRun=$pipeline_run" --all-containers=true --tail=-1 >"$result_dir/pods.log" 2>&1 || true
|
|
write_result true succeeded "$condition"
|
|
exit 0
|
|
;;
|
|
False*)
|
|
kubectl get "pipelinerun/$pipeline_run" -n unidesk-ci -o json >"$result_dir/pipelinerun.json" || true
|
|
kubectl get taskrun -n unidesk-ci -l "tekton.dev/pipelineRun=$pipeline_run" -o json >"$result_dir/taskruns.json" || true
|
|
kubectl logs -n unidesk-ci -l "tekton.dev/pipelineRun=$pipeline_run" --all-containers=true --tail=-1 >"$result_dir/pods.log" 2>&1 || true
|
|
write_result false failed "$condition"
|
|
exit 1
|
|
;;
|
|
esac
|
|
sleep 2
|
|
done
|
|
|
|
kubectl get "pipelinerun/$pipeline_run" -n unidesk-ci -o json >"$result_dir/pipelinerun.json" || true
|
|
kubectl get taskrun -n unidesk-ci -l "tekton.dev/pipelineRun=$pipeline_run" -o json >"$result_dir/taskruns.json" || true
|
|
kubectl logs -n unidesk-ci -l "tekton.dev/pipelineRun=$pipeline_run" --all-containers=true --tail=-1 >"$result_dir/pods.log" 2>&1 || true
|
|
write_result false timeout "Timed out waiting for pipelinerun/$pipeline_run"
|
|
exit 124
|