diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e71568..8f155d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/). +## [Unreleased] + +### Added + +- `easy proxy verify` — checks the proxy container is actually running and + surfaces the startup error if it crashed. `easy proxy create` now runs this + check automatically, so it no longer reports success when nginx failed to + start. + ## [2.1.0] — 2026-05-18 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index bb4f78c..add31a9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -147,6 +147,7 @@ easy proxy status # → container ID se running | `easy proxy status` | Container ID se running, vuoto se fermo | | `easy proxy id` | Container ID (`docker ps` per nome `easy-proxy`, anche se fermo) | | `easy proxy doctor` | Diagnosi read-only: vhost non-standard, `nginx -t`, reti del proxy | +| `easy proxy verify` | Verifica che il proxy sia davvero up; `create` la esegue da solo | | `easy proxy attach\|detach ` | Collega/scollega un container alla rete edge `EASY_PROXY_NETWORK` | | `easy proxy networks [prune]` | Mostra le reti del proxy; `prune` scollega quelle non-edge | | `easy proxy start/stop/restart` | Ciclo container | diff --git a/README.md b/README.md index c6c5a04..d752506 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Run `easy proxy help` for the full list. | `easy proxy status` | Container id if running, empty if stopped | | `easy proxy id` | Container id (running or stopped) | | `easy proxy doctor` | Read-only diagnostic: non-standard vhosts, `nginx -t`, proxy networks | +| `easy proxy verify` | Check the proxy is actually running; `create` runs it automatically | | `easy proxy attach` / `detach ` | Connect/disconnect a site container to the edge network | | `easy proxy networks [prune]` | Show the proxy's networks; `prune` disconnects non-edge ones | | `easy proxy start` / `stop` / `restart` | Container lifecycle | diff --git a/commands/proxy.sh b/commands/proxy.sh index 0c98835..1360616 100644 --- a/commands/proxy.sh +++ b/commands/proxy.sh @@ -46,6 +46,7 @@ function __easy_command_proxy_help { echo " easy proxy id" echo " easy proxy status" echo " easy proxy doctor" + echo " easy proxy verify" echo " easy proxy attach " echo " easy proxy detach " echo " easy proxy networks [prune]" @@ -230,6 +231,10 @@ chmod 600 /etc/letsencrypt/ionos.ini" __easy_command_proxy_doctor return $? fi + if [[ "verify" == "$2" ]]; then + __easy_command_proxy_verify + return $? + fi if [[ "attach" == "$2" ]]; then __easy_command_proxy_attach "$3" return $? @@ -251,6 +256,23 @@ chmod 600 /etc/letsencrypt/ionos.ini" fi } +# Check the proxy container is actually running; on failure surface the reason. +function __easy_command_proxy_verify { + if [[ -n "$(easy proxy status)" ]]; then + echo "easy proxy: running" + return 0 + fi + echo "easy proxy: NOT running" + if [[ -n "$(easy proxy id)" ]]; then + echo "the container exited — last log lines:" + docker logs --tail 15 "${EASY_PROXY_NAME}" 2>&1 | sed 's/^/ /' + echo "fix the cause, then 'easy proxy destroy' and 'easy proxy create' again." + else + echo "no ${EASY_PROXY_NAME} container — run 'easy proxy create'." + fi + return 1 +} + function __easy_command_proxy_create { if [[ -n "$(easy proxy id)" ]]; then echo "There is already an easy proxy instance named ${EASY_PROXY_NAME}" @@ -278,8 +300,11 @@ function __easy_command_proxy_create { -v "${EASY_DIR}/easyhome:/usr/local/share/easy" \ -p 80:80 \ -p 443:443 \ - -t ethiclab/nginx-easy - return $? + -t ethiclab/nginx-easy || return $? + # `docker run` only starts the container — nginx can still crash on a bad + # config. Give it a moment, then verify the proxy is actually serving. + sleep "${EASY_VERIFY_DELAY:-2}" + __easy_command_proxy_verify } # Read-only pre-flight diagnostic: static vhost analysis (host-side) plus, diff --git a/test/network.bats b/test/network.bats index 44eca8a..8111fa4 100644 --- a/test/network.bats +++ b/test/network.bats @@ -16,7 +16,8 @@ setup() { easy_setup; } @test "easy proxy create joins EASY_PROXY_NETWORK and auto-creates it" { export EASY_PROXY_NETWORK=ethicnet export DOCKER_LOG="$BATS_TEST_TMPDIR/docker.log" - mock_docker_record + mock_docker_lifecycle + export DOCKER_PROXY_HEALTHY=1 run easy proxy create [ "$status" -eq 0 ] grep -q "network create ethicnet" "$DOCKER_LOG" @@ -25,7 +26,8 @@ setup() { easy_setup; } @test "easy proxy create stays on the default network when EASY_PROXY_NETWORK is unset" { export DOCKER_LOG="$BATS_TEST_TMPDIR/docker.log" - mock_docker_record + mock_docker_lifecycle + export DOCKER_PROXY_HEALTHY=1 run easy proxy create [ "$status" -eq 0 ] ! grep -q -- "--network" "$DOCKER_LOG" diff --git a/test/proxy.bats b/test/proxy.bats index 9f05ffc..310afaa 100644 --- a/test/proxy.bats +++ b/test/proxy.bats @@ -59,7 +59,8 @@ setup() { easy_setup; } } @test "easy proxy create writes no state file into the install dir (#5)" { - mock_docker_stopped + mock_docker_lifecycle + export DOCKER_PROXY_HEALTHY=1 chmod -w "$EASY_CLI_DIR" run easy proxy create chmod +w "$EASY_CLI_DIR" diff --git a/test/test_helper.bash b/test/test_helper.bash index a714c54..a7a93a7 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -28,6 +28,30 @@ easy_setup() { # Deterministic: never inherit real credentials/config from the host shell. unset IONOS_API_KEY IONOS_API_SECRET EASY_LETSENCRYPT_EMAIL EASY_LETSENCRYPT_DOMAIN EASY_PROXY_NETWORK + # Skip the post-create verify wait — the mocks settle instantly. + export EASY_VERIFY_DELAY=0 +} + +# Stateful `docker` mock for `create` + `verify`. `run` marks the container +# created; `ps -a` (id) then reports it; `ps` (running) reports it only when +# DOCKER_PROXY_HEALTHY is set — leave it unset to simulate a startup crash. +mock_docker_lifecycle() { + export DOCKER_STATE="$BATS_TEST_TMPDIR/docker-state" + rm -f "$DOCKER_STATE" + cat > "$MOCK_BIN/docker" <<'MOCK' +#!/usr/bin/env bash +echo "$*" >> "${DOCKER_LOG:-/dev/null}" +case "$1 $2" in + "run "*) echo created > "$DOCKER_STATE"; echo "deadbeefcafe1234" ;; + "ps -a"*) [ -f "$DOCKER_STATE" ] && echo "deadbeefcafe1234" ;; + "ps "*) { [ -f "$DOCKER_STATE" ] && [ -n "$DOCKER_PROXY_HEALTHY" ]; } && echo "deadbeefcafe1234" ;; + "logs "*) echo 'nginx: [emerg] host not found in upstream "x"' ;; + "network inspect"*) exit 1 ;; + "stop "*|"rm "*) rm -f "$DOCKER_STATE" ;; + *) exit 0 ;; +esac +MOCK + chmod +x "$MOCK_BIN/docker" } # Mock `docker` so `docker ps` reports a fake running easy-proxy container. diff --git a/test/verify.bats b/test/verify.bats new file mode 100644 index 0000000..c0768d5 --- /dev/null +++ b/test/verify.bats @@ -0,0 +1,49 @@ +#!/usr/bin/env bats +# Tests for `easy proxy verify` and the auto-verify wired into `easy proxy create`. + +load test_helper + +setup() { easy_setup; } + +@test "easy proxy help lists verify" { + run easy proxy help + [ "$status" -eq 0 ] + [[ "$output" == *"easy proxy verify"* ]] +} + +@test "easy proxy verify exits 0 when the proxy is running" { + mock_docker_running + run easy proxy verify + [ "$status" -eq 0 ] + [[ "$output" == *"running"* ]] +} + +@test "easy proxy verify fails when there is no proxy container" { + mock_docker_stopped + run easy proxy verify + [ "$status" -ne 0 ] + [[ "$output" == *"NOT running"* ]] +} + +@test "easy proxy verify surfaces the startup error when the container has exited" { + mock_docker_lifecycle # DOCKER_PROXY_HEALTHY unset → container not running + touch "$DOCKER_STATE" # ...but a container exists + run easy proxy verify + [ "$status" -ne 0 ] + [[ "$output" == *"emerg"* ]] +} + +@test "easy proxy create auto-verifies a healthy proxy" { + mock_docker_lifecycle + export DOCKER_PROXY_HEALTHY=1 + run easy proxy create + [ "$status" -eq 0 ] + [[ "$output" == *"running"* ]] +} + +@test "easy proxy create fails when the proxy crashes on startup" { + mock_docker_lifecycle # no DOCKER_PROXY_HEALTHY → nginx 'crashes' + run easy proxy create + [ "$status" -ne 0 ] + [[ "$output" == *"emerg"* ]] +}