teleo-infrastructure/scripts/run_approve_claim_isolated_container_canary.sh

275 lines
9 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source_container="${SOURCE_CONTAINER:-teleo-pg}"
source_db="${SOURCE_DB:-teleo}"
service="${LEO_SERVICE:-leoclean-gateway.service}"
image="${POSTGRES_CANARY_IMAGE:-postgres:16}"
source_tier="${SOURCE_TIER:-live-readonly}"
normalization_json=""
output="${repo_root}/docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json"
lifecycle_receipt_output=""
rollback_sql_output=""
while [[ $# -gt 0 ]]; do
case "$1" in
--normalization-json)
normalization_json="$2"
shift 2
;;
--output)
output="$2"
shift 2
;;
--lifecycle-receipt-output)
lifecycle_receipt_output="$2"
shift 2
;;
--rollback-sql-output)
rollback_sql_output="$2"
shift 2
;;
--source-container)
source_container="$2"
shift 2
;;
--source-db)
source_db="$2"
shift 2
;;
--service)
service="$2"
shift 2
;;
--source-tier)
source_tier="$2"
shift 2
;;
*)
echo "unknown argument: $1" >&2
exit 2
;;
esac
done
if [[ "$source_tier" != "isolated-local" && "$source_tier" != "live-readonly" ]]; then
echo "--source-tier must be isolated-local or live-readonly" >&2
exit 2
fi
source_network_mode="$(docker inspect -f '{{.HostConfig.NetworkMode}}' "$source_container")"
source_canary_label="$(
docker inspect -f '{{index .Config.Labels "livingip.canary"}}' "$source_container"
)"
if [[ "$source_tier" == "isolated-local" ]]; then
if [[ "$source_network_mode" != "none" || "$source_canary_label" != "working-leo-approved-source" ]]; then
echo "isolated-local source must be networkless and labeled livingip.canary=working-leo-approved-source" >&2
exit 2
fi
fi
container="working-leo-approve-claim-$RANDOM-$$"
workdir="/tmp/${container}"
apply_password="isolated-apply-${RANDOM}-${RANDOM}"
review_password="isolated-review-${RANDOM}-${RANDOM}"
cleanup() {
docker rm -f "$container" >/dev/null 2>&1 || true
rm -rf "$workdir"
}
trap cleanup EXIT
source_counts() {
docker exec "$source_container" psql -U postgres -d "$source_db" -Atq \
-v ON_ERROR_STOP=1 -c "begin transaction read only;
select jsonb_build_object(
'public.claims',(select count(*) from public.claims),
'public.sources',(select count(*) from public.sources),
'public.claim_evidence',(select count(*) from public.claim_evidence),
'public.claim_edges',(select count(*) from public.claim_edges),
'public.reasoning_tools',(select count(*) from public.reasoning_tools),
'kb_stage.kb_proposals',(select count(*) from kb_stage.kb_proposals)
)::text;
rollback;"
}
service_state() {
if ! command -v systemctl >/dev/null 2>&1; then
printf 'Available=false\nReason=systemctl_unavailable\n'
return 0
fi
local output
if output="$(systemctl show "$service" --no-pager \
-p ActiveState -p SubState -p MainPID -p NRestarts -p ExecMainStartTimestamp 2>&1)"; then
printf 'Available=true\n%s\n' "$output"
else
printf 'Available=false\nReason=service_readback_failed\n'
fi
}
mkdir -p "$workdir" "$(dirname "$output")"
if [[ -n "$lifecycle_receipt_output" ]]; then
mkdir -p "$(dirname "$lifecycle_receipt_output")"
fi
if [[ -n "$rollback_sql_output" ]]; then
mkdir -p "$(dirname "$rollback_sql_output")"
fi
source_counts_before="$(source_counts)"
service_before="$(service_state)"
docker run -d --name "$container" \
--network none \
--label livingip.canary=proposal-apply-lifecycle \
--label "livingip.canary.instance=${container}" \
--tmpfs /var/lib/postgresql/data:rw,nosuid,size=512m \
-e POSTGRES_PASSWORD="isolated-postgres-${RANDOM}-${RANDOM}" \
-e POSTGRES_DB=teleo \
"$image" >/dev/null
ready_streak=0
for _ in $(seq 1 80); do
if docker exec "$container" pg_isready -U postgres -d teleo >/dev/null 2>&1; then
ready_streak=$((ready_streak + 1))
if [[ "$ready_streak" -ge 2 ]]; then
break
fi
else
ready_streak=0
fi
sleep 0.25
done
if [[ "$ready_streak" -lt 2 ]]; then
echo "disposable Postgres did not become stably ready" >&2
exit 1
fi
container_network_mode="$(docker inspect -f '{{.HostConfig.NetworkMode}}' "$container")"
container_mounts_json="$(docker inspect -f '{{json .Mounts}}' "$container")"
container_canary_label="$(
docker inspect -f '{{index .Config.Labels "livingip.canary"}}' "$container"
)"
container_instance_label="$(
docker inspect -f '{{index .Config.Labels "livingip.canary.instance"}}' "$container"
)"
docker exec "$source_container" pg_dump -U postgres -d "$source_db" \
--schema-only --no-owner --no-privileges >"$workdir/schema.sql"
docker exec "$source_container" pg_dump -U postgres -d "$source_db" \
--data-only --no-owner --no-privileges \
--table=public.agents --table=public.claims >"$workdir/reference-data.sql"
docker cp "$workdir/schema.sql" "$container:/tmp/schema.sql"
docker cp "$workdir/reference-data.sql" "$container:/tmp/reference-data.sql"
docker exec -i "$container" psql -U postgres -d postgres -v ON_ERROR_STOP=1 -q <<SQL
create role kb_apply login noinherit nosuperuser nocreatedb nocreaterole
noreplication nobypassrls password '${apply_password}';
create role kb_review login noinherit nosuperuser nocreatedb nocreaterole
noreplication nobypassrls password '${review_password}';
create role kb_gate_owner nologin noinherit nosuperuser nocreatedb nocreaterole
noreplication nobypassrls;
SQL
docker exec "$container" psql -U postgres -d teleo -v ON_ERROR_STOP=1 -q \
-f /tmp/schema.sql
docker exec "$container" psql -U postgres -d teleo -v ON_ERROR_STOP=1 -q \
-f /tmp/reference-data.sql
docker exec "$container" psql -U postgres -d teleo -v ON_ERROR_STOP=1 -q \
-c "insert into public.agents (id, handle, kind)
values ('99999999-9999-9999-9999-999999999999', 'm3ta', 'human')
on conflict (handle) do nothing"
cat >"$workdir/kb-apply.env" <<EOF
KB_APPLY_PASSWORD=${apply_password}
EOF
cat >"$workdir/kb-review.env" <<EOF
KB_REVIEW_PASSWORD=${review_password}
EOF
chmod 600 "$workdir/kb-apply.env" "$workdir/kb-review.env"
normalization_args=()
if [[ -n "$normalization_json" ]]; then
normalization_args=(--normalization-json "$normalization_json")
fi
artifact_args=()
if [[ -n "$lifecycle_receipt_output" ]]; then
artifact_args+=(--lifecycle-receipt-output "$lifecycle_receipt_output")
fi
if [[ -n "$rollback_sql_output" ]]; then
artifact_args+=(--rollback-sql-output "$rollback_sql_output")
fi
set +e
python3 "$repo_root/scripts/run_approve_claim_clone_canary.py" \
--container "$container" \
--source-db teleo \
--database-prefix teleo_approve_claim_isolated \
--service "$service" \
--approve-script "$repo_root/scripts/approve_proposal.py" \
--apply-script "$repo_root/scripts/apply_proposal.py" \
--prereqs "$repo_root/scripts/kb_apply_prereqs.sql" \
--review-secrets-file "$workdir/kb-review.env" \
--secrets-file "$workdir/kb-apply.env" \
"${normalization_args[@]}" \
"${artifact_args[@]}" \
--output "$output" \
>"$workdir/stdout.json" 2>"$workdir/stderr.log"
canary_rc=$?
set -e
set +e
docker rm -f "$container" >/dev/null 2>&1
outer_container_remove_rc=$?
rm -rf "$workdir"
outer_workdir_remove_rc=$?
container_name_readback="$(
docker container ls -a --filter "name=^/${container}$" --format '{{.Names}}' 2>&1
)"
container_name_readback_rc=$?
container_label_readback="$(
docker container ls -a \
--filter "label=livingip.canary=proposal-apply-lifecycle" \
--filter "label=livingip.canary.instance=${container}" \
--format '{{.Names}}' 2>&1
)"
container_label_readback_rc=$?
if [[ "$container_label_readback_rc" -eq 0 ]]; then
container_label_orphan_count="$(
awk 'NF { count += 1 } END { print count + 0 }' <<<"$container_label_readback"
)"
else
container_label_orphan_count=-1
fi
if [[ -e "$workdir" || -L "$workdir" ]]; then
outer_workdir_lexists=true
else
outer_workdir_lexists=false
fi
set -e
trap - EXIT
source_counts_after="$(source_counts)"
service_after="$(service_state)"
SOURCE_COUNTS_BEFORE="$source_counts_before" \
SOURCE_COUNTS_AFTER="$source_counts_after" \
SOURCE_CONTAINER="$source_container" \
SOURCE_DB="$source_db" \
SERVICE_BEFORE="$service_before" \
SERVICE_AFTER="$service_after" \
REPO_ROOT="$repo_root" \
OUTER_CONTAINER_REMOVE_RC="$outer_container_remove_rc" \
OUTER_WORKDIR_REMOVE_RC="$outer_workdir_remove_rc" \
CONTAINER_NAME_READBACK="$container_name_readback" \
CONTAINER_NAME_READBACK_RC="$container_name_readback_rc" \
CONTAINER_LABEL_READBACK="$container_label_readback" \
CONTAINER_LABEL_READBACK_RC="$container_label_readback_rc" \
CONTAINER_LABEL_ORPHAN_COUNT="$container_label_orphan_count" \
OUTER_WORKDIR_LEXISTS="$outer_workdir_lexists" \
CANARY_RC="$canary_rc" \
SOURCE_TIER="$source_tier" \
SOURCE_NETWORK_MODE="$source_network_mode" \
SOURCE_CANARY_LABEL="$source_canary_label" \
CANARY_NETWORK_MODE="$container_network_mode" \
CANARY_MOUNTS_JSON="$container_mounts_json" \
CANARY_LABEL="$container_canary_label" \
CANARY_INSTANCE_LABEL="$container_instance_label" \
python3 "$repo_root/scripts/finalize_approve_claim_isolated_canary.py" "$output"