From 97d767a947603d556bc8b4ae0633289a8eba4a1b Mon Sep 17 00:00:00 2001 From: Mustafa Zeydani Date: Thu, 20 Aug 2026 01:02:46 +0300 Subject: [PATCH] fix(deploy): use approved container diagnostics --- devops/production/bin/deploy.sh | 33 +++++++++++-------- .../bin/test_production_deploy_contract.py | 8 +++++ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/devops/production/bin/deploy.sh b/devops/production/bin/deploy.sh index 6ebeaf1..fd19596 100755 --- a/devops/production/bin/deploy.sh +++ b/devops/production/bin/deploy.sh @@ -23,6 +23,7 @@ POSTGRES_CONTAINER="infra-postgres" REDIS_CONTAINER="opensyria-production-redis" EDGE_NETWORK="syr-staging-edge" DATA_NETWORK="opensyria-production-data" +COMPOSE_PS_FORMAT='table {{.Name}}\t{{.Image}}\t{{.State}}\t{{.Health}}' PUBLIC_HOST="api.opensyria.org" HEALTH_TIMEOUT_SECONDS="${HEALTH_TIMEOUT_SECONDS:-240}" DRAIN_SECONDS="${DRAIN_SECONDS:-30}" @@ -175,21 +176,25 @@ reload_nginx() { && docker_cmd exec "${NGINX_CONTAINER}" nginx -s reload } -service_container_id() { - compose ps -q "$(service_for_slot "$1")" +compose_status() { + compose ps --format "${COMPOSE_PS_FORMAT}" +} + +container_is_running() { + docker_cmd ps --format '{{.Names}}' | grep -Fxq "$1" } service_is_healthy() { local slot="$1" - local container_id health + local container_name - container_id="$(service_container_id "${slot}")" - [[ -n "${container_id}" ]] || return 1 - health="$( - docker_cmd inspect "${container_id}" \ - --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' - )" - [[ "${health}" == "healthy" ]] + container_name="opensyria-production-api-${slot}" + compose_status | awk -v container_name="${container_name}" ' + NR > 1 && $1 == container_name && $3 == "running" && $4 == "healthy" { + healthy += 1 + } + END { exit(healthy == 1 ? 0 : 1) } + ' } wait_for_service_health() { @@ -201,7 +206,7 @@ wait_for_service_health() { while ! service_is_healthy "${slot}"; do now="$(date +%s)" if ((now - started_at >= HEALTH_TIMEOUT_SECONDS)); then - compose ps "${service}" >&2 || true + compose_status >&2 || true compose logs --tail=150 "${service}" >&2 || true fail "Timed out waiting for ${service} to become healthy" fi @@ -559,9 +564,9 @@ prepare_release() { || fail "External Docker network ${EDGE_NETWORK} is missing" docker_cmd network-exists "${DATA_NETWORK}" >/dev/null \ || fail "External Docker network ${DATA_NETWORK} is missing" - docker_cmd inspect "${POSTGRES_CONTAINER}" >/dev/null \ + container_is_running "${POSTGRES_CONTAINER}" \ || fail "Shared PostgreSQL container is missing" - docker_cmd inspect "${REDIS_CONTAINER}" >/dev/null \ + container_is_running "${REDIS_CONTAINER}" \ || fail "Dedicated OpenSyria Redis container is missing" routed_slot="$(current_upstream_slot)" @@ -723,7 +728,7 @@ show_status() { echo "Pending rollout: no" fi if [[ -f "${COMPOSE_ENV_FILE}" && -f "${RUNTIME_ENV_FILE}" ]]; then - compose ps + compose_status fi } diff --git a/devops/production/bin/test_production_deploy_contract.py b/devops/production/bin/test_production_deploy_contract.py index aa20d3f..a59a60b 100644 --- a/devops/production/bin/test_production_deploy_contract.py +++ b/devops/production/bin/test_production_deploy_contract.py @@ -13,6 +13,14 @@ def test_uses_approved_network_diagnostics(self) -> None: self.assertIn('docker_cmd network-exists "${DATA_NETWORK}"', script) self.assertNotIn("docker_cmd network inspect", script) + def test_uses_approved_container_diagnostics(self) -> None: + script = DEPLOY_SCRIPT.read_text(encoding="utf-8") + + self.assertIn("docker_cmd ps --format '{{.Names}}'", script) + self.assertIn('compose ps --format "${COMPOSE_PS_FORMAT}"', script) + self.assertNotIn("docker_cmd inspect", script) + self.assertNotIn("compose ps -q", script) + if __name__ == "__main__": unittest.main()