Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 42 additions & 16 deletions install/hiclaw-verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#
# Usage:
# bash install/hiclaw-verify.sh [container_name] # default: agentteams-manager
# AGENTTEAMS_VERIFY_INFRA_CONTAINER=<name> # default: agentteams-controller
#
# Runs 6 read-only reachability checks and prints PASS/FAIL per check.
# Exit code: 0 if all pass, 1 if any fail.
Expand All @@ -22,8 +23,9 @@
# kubectl get pod -l app=agentteams-manager -o jsonpath='{.items[0].metadata.name}'
#
# 2. Internal service checks (checks #2, #3, #6)
# These use `docker exec ... curl 127.0.0.1:PORT` which works because all
# services share a single container network namespace.
# Local embedded installs route infrastructure checks to the controller
# container and the Agent check to the Manager container. Legacy installs
# fall back to the Manager container for all three checks.
# In K8s each service is a separate Pod/Service; replace with:
# `kubectl exec <manager-pod> -- curl http://<service-name>.<ns>.svc:PORT`
# or use `kubectl port-forward svc/<name> LOCAL:REMOTE` for a one-shot probe.
Expand All @@ -39,7 +41,8 @@

# No set -e: each check is independent; failures do not abort subsequent checks.

CONTAINER="${1:-agentteams-manager}"
MANAGER_CONTAINER="${1:-agentteams-manager}"
INFRA_CONTAINER="${AGENTTEAMS_VERIFY_INFRA_CONTAINER:-agentteams-controller}"

# ---------- Docker/Podman detection ----------
# TODO(k8s): extend to three-way detection (docker / podman / kubectl)
Expand All @@ -52,13 +55,36 @@ if ! docker version >/dev/null 2>&1; then
fi
fi

# ---------- Port/config detection from container env ----------
# ---------- Local topology and port/config detection ----------
# TODO(k8s): replace printenv-based detection with kubectl-based service
# discovery, or accept GATEWAY_URL / CONSOLE_URL env vars directly.

container_env=$("${DOCKER_CMD}" exec "${CONTAINER}" printenv 2>/dev/null) || container_env=""
PORT_GATEWAY=$(echo "$container_env" | grep ^AGENTTEAMS_PORT_GATEWAY= | cut -d= -f2-)
PORT_CONSOLE=$(echo "$container_env" | grep ^AGENTTEAMS_PORT_CONSOLE= | cut -d= -f2-)
running_containers=$("${DOCKER_CMD}" ps --format '{{.Names}}' 2>/dev/null) || running_containers=""
SERVICE_CONTAINER="${MANAGER_CONTAINER}"
if printf '%s\n' "${running_containers}" | grep -Fxq "${INFRA_CONTAINER}"; then
SERVICE_CONTAINER="${INFRA_CONTAINER}"
fi

manager_env=$("${DOCKER_CMD}" exec "${MANAGER_CONTAINER}" printenv 2>/dev/null) || manager_env=""
if [ "${SERVICE_CONTAINER}" = "${MANAGER_CONTAINER}" ]; then
service_env="${manager_env}"
else
service_env=$("${DOCKER_CMD}" exec "${SERVICE_CONTAINER}" printenv 2>/dev/null) || service_env=""
fi

mapped_host_port() {
local container="$1"
local container_port="$2"
local mapping

mapping=$("${DOCKER_CMD}" port "${container}" "${container_port}/tcp" 2>/dev/null | head -1) || mapping=""
printf '%s\n' "${mapping}" | awk -F: 'NF > 1 { print $NF; exit }'
}

PORT_GATEWAY=$(echo "${service_env}" | grep ^AGENTTEAMS_PORT_GATEWAY= | cut -d= -f2-)
PORT_CONSOLE=$(echo "${service_env}" | grep ^AGENTTEAMS_PORT_CONSOLE= | cut -d= -f2-)
PORT_GATEWAY="${PORT_GATEWAY:-$(mapped_host_port "${SERVICE_CONTAINER}" 8080)}"
PORT_CONSOLE="${PORT_CONSOLE:-$(mapped_host_port "${SERVICE_CONTAINER}" 8001)}"
PORT_GATEWAY="${PORT_GATEWAY:-18080}"
PORT_CONSOLE="${PORT_CONSOLE:-18001}"

Expand All @@ -85,16 +111,16 @@ echo "==> AgentTeams Post-Install Verification"
# 1. Manager container running
# TODO(k8s): replace with `kubectl get pod -l app=agentteams-manager` and check
# that at least one pod is in Running phase (not just Pending/CrashLoopBackOff).
if "${DOCKER_CMD}" ps --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER}$"; then
if printf '%s\n' "${running_containers}" | grep -Fxq "${MANAGER_CONTAINER}"; then
check_pass "Manager container running"
else
check_fail "Manager container running (container '${CONTAINER}' not found in docker ps)"
check_fail "Manager container running (container '${MANAGER_CONTAINER}' not found in ${DOCKER_CMD} ps)"
fi

# 2. MinIO health check (internal via docker exec)
# 2. MinIO health check (internal via infrastructure container)
# TODO(k8s): replace with `kubectl exec <manager-pod> -- curl http://minio.<ns>.svc:9000/minio/health/live`
# or probe the MinIO Service ClusterIP directly if network policy allows.
minio_status=$("${DOCKER_CMD}" exec "${CONTAINER}" \
minio_status=$("${DOCKER_CMD}" exec "${SERVICE_CONTAINER}" \
curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
"http://127.0.0.1:9000/minio/health/live" 2>/dev/null) || minio_status="000"
if [ "${minio_status}" = "200" ]; then
Expand All @@ -103,9 +129,9 @@ else
check_fail "MinIO health check (HTTP ${minio_status})"
fi

# 3. Matrix API reachable (internal via docker exec)
# 3. Matrix API reachable (internal via infrastructure container)
# TODO(k8s): replace with `kubectl exec <manager-pod> -- curl http://matrix.<ns>.svc:6167/_matrix/client/versions`
matrix_status=$("${DOCKER_CMD}" exec "${CONTAINER}" \
matrix_status=$("${DOCKER_CMD}" exec "${SERVICE_CONTAINER}" \
curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
"http://127.0.0.1:6167/_matrix/client/versions" 2>/dev/null) || matrix_status="000"
if [ "${matrix_status}" = "200" ]; then
Expand Down Expand Up @@ -139,12 +165,12 @@ fi
# 6. Manager Agent healthy (runtime-aware check)
# TODO(k8s): replace with `kubectl exec <manager-pod> -- <health-check-command>`
# Pod name must be resolved dynamically before this call.
MANAGER_RUNTIME=$(echo "$container_env" | grep ^AGENTTEAMS_MANAGER_RUNTIME= | cut -d= -f2-)
MANAGER_RUNTIME=$(echo "${manager_env}" | grep ^AGENTTEAMS_MANAGER_RUNTIME= | cut -d= -f2-)
MANAGER_RUNTIME="${MANAGER_RUNTIME:-openclaw}"

if [ "${MANAGER_RUNTIME}" = "copaw" ]; then
# CoPaw: check app API health endpoint
agent_status=$("${DOCKER_CMD}" exec "${CONTAINER}" \
agent_status=$("${DOCKER_CMD}" exec "${MANAGER_CONTAINER}" \
curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
"http://127.0.0.1:18799/health" 2>/dev/null) || agent_status="000"
if [ "${agent_status}" = "200" ]; then
Expand All @@ -154,7 +180,7 @@ if [ "${MANAGER_RUNTIME}" = "copaw" ]; then
fi
else
# OpenClaw: check gateway health
agent_output=$("${DOCKER_CMD}" exec "${CONTAINER}" \
agent_output=$("${DOCKER_CMD}" exec "${MANAGER_CONTAINER}" \
openclaw gateway health --json 2>/dev/null) || agent_output=""
if echo "${agent_output}" | grep -q '"ok"'; then
check_pass "OpenClaw Agent healthy"
Expand Down
157 changes: 157 additions & 0 deletions install/tests/test-verify-container-routing.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#!/usr/bin/env bash

set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
VERIFY_SCRIPT="${ROOT_DIR}/install/hiclaw-verify.sh"
TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentteams-verify-routing.XXXXXX")"
trap 'rm -rf "${TEST_ROOT}"' EXIT

FAKE_BIN="${TEST_ROOT}/bin"
CAPTURE="${TEST_ROOT}/exec-calls"
mkdir -p "${FAKE_BIN}"

cat > "${FAKE_BIN}/docker" <<'EOF'
#!/usr/bin/env bash
set -e

mode="${VERIFY_FAKE_MODE:?}"
case "${1:-}" in
version)
exit 0
;;
ps)
if [ "${mode}" = "embedded" ]; then
printf '%s\n' agentteams-controller agentteams-manager
else
printf '%s\n' agentteams-manager
fi
exit 0
;;
port)
container="${2:-}"
container_port="${3:-}"
printf 'port %s %s\n' "${container}" "${container_port}" >> "${VERIFY_FAKE_CAPTURE}"
case "${container_port}" in
8080/tcp) printf '127.0.0.1:19080\n' ;;
8001/tcp) printf '127.0.0.1:19001\n' ;;
*) exit 1 ;;
esac
exit 0
;;
exec)
container="${2:-}"
command="${3:-}"
case "${command}" in
printenv)
if [ "${container}" = "agentteams-manager" ]; then
printf 'AGENTTEAMS_MANAGER_RUNTIME=copaw\n'
if [ "${mode}" = "legacy" ]; then
printf 'AGENTTEAMS_PORT_GATEWAY=18080\n'
printf 'AGENTTEAMS_PORT_CONSOLE=18001\n'
fi
elif [ "${container}" = "agentteams-controller" ]; then
:
fi
exit 0
;;
curl)
url="${!#}"
printf '%s %s\n' "${container}" "${url}" >> "${VERIFY_FAKE_CAPTURE}"
case "${url}" in
*:9000/minio/health/live|*:6167/_matrix/client/versions)
if { [ "${mode}" = "embedded" ] && [ "${container}" = "agentteams-controller" ]; } || \
{ [ "${mode}" = "legacy" ] && [ "${container}" = "agentteams-manager" ]; }; then
printf '200'
else
printf '000'
fi
;;
*:18799/health)
if [ "${container}" = "agentteams-manager" ]; then
printf '200'
else
printf '000'
fi
;;
*)
printf '000'
;;
esac
exit 0
;;
esac
;;
esac

printf 'unexpected docker invocation: %s\n' "$*" >&2
exit 1
EOF

cat > "${FAKE_BIN}/curl" <<'EOF'
#!/usr/bin/env bash
printf 'host %s\n' "${!#}" >> "${VERIFY_FAKE_CAPTURE}"
printf '200'
EOF

chmod +x "${FAKE_BIN}/docker" "${FAKE_BIN}/curl"

PASS=0
FAIL=0
RUN_OUTPUT=""
RUN_RC=0

pass() {
printf 'PASS: %s\n' "$1"
PASS=$((PASS + 1))
}

fail() {
printf 'FAIL: %s\n' "$1" >&2
FAIL=$((FAIL + 1))
}

run_verify() {
local mode="$1"
rm -f "${CAPTURE}"
set +e
RUN_OUTPUT="$(
env \
PATH="${FAKE_BIN}:${PATH}" \
VERIFY_FAKE_MODE="${mode}" \
VERIFY_FAKE_CAPTURE="${CAPTURE}" \
AGENTTEAMS_VERIFY_INFRA_CONTAINER=agentteams-controller \
bash "${VERIFY_SCRIPT}" 2>&1
)"
RUN_RC=$?
set -e
}

run_verify embedded
if [ "${RUN_RC}" -eq 0 ] \
&& [[ "${RUN_OUTPUT}" == *"Result: 6/6 passed"* ]] \
&& grep -Fq 'agentteams-controller http://127.0.0.1:9000/minio/health/live' "${CAPTURE}" \
&& grep -Fq 'agentteams-controller http://127.0.0.1:6167/_matrix/client/versions' "${CAPTURE}" \
&& grep -Fq 'agentteams-manager http://127.0.0.1:18799/health' "${CAPTURE}" \
&& grep -Fq 'host http://127.0.0.1:19080/' "${CAPTURE}" \
&& grep -Fq 'host http://127.0.0.1:19001/' "${CAPTURE}"; then
pass "embedded verification routes infrastructure and Agent checks correctly"
else
fail "embedded verification routing: rc=${RUN_RC}, output=${RUN_OUTPUT}"
fi

run_verify legacy
if [ "${RUN_RC}" -eq 0 ] \
&& [[ "${RUN_OUTPUT}" == *"Result: 6/6 passed"* ]] \
&& grep -Fq 'agentteams-manager http://127.0.0.1:9000/minio/health/live' "${CAPTURE}" \
&& grep -Fq 'agentteams-manager http://127.0.0.1:6167/_matrix/client/versions' "${CAPTURE}" \
&& ! grep -Fq 'agentteams-controller ' "${CAPTURE}"; then
pass "legacy verification keeps infrastructure checks in the Manager container"
else
fail "legacy verification routing: rc=${RUN_RC}, output=${RUN_OUTPUT}"
fi

printf '\nResults: %d passed, %d failed\n' "${PASS}" "${FAIL}"
if [ "${FAIL}" -ne 0 ]; then
exit 1
fi
Loading