diff --git a/Dockerfile b/Dockerfile index 3aff504..601555a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,8 +52,12 @@ RUN if [ "$DNSMASQ_VERSION" = "distro" ]; then \ && apt-get autoremove -y --purge \ && rm -rf /var/lib/apt/lists/*; \ fi -COPY scripts/entrypoint.sh scripts/dnsmasq-status.sh . +COPY scripts/entrypoint.sh scripts/dnsmasq-status.sh ./ RUN chmod +x entrypoint.sh dnsmasq-status.sh +# curl for HEALTHCHECK (aspnet image has no curl/wget) +RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/* # .NET publish last - only this layer invalidates when code changes COPY --from=publish /app/publish . ENTRYPOINT ["./entrypoint.sh"] +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD curl -f -s http://localhost:8080/healthz/ready || exit 1 diff --git a/README.md b/README.md index 94b96b3..375f026 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ A self-hosted web UI for managing [dnsmasq](https://thekelleys.org.uk/dnsmasq/do - **DHCP host entries** (reservations) if you use DHCP; edit in the UI, written into the managed config. - **Reload dnsmasq** after config changes (configurable command, e.g. `systemctl reload dnsmasq` or `pkill -HUP -x dnsmasq`). - Optional **status** and **recent logs** commands (e.g. systemctl/journalctl or custom scripts) shown on the Dnsmasq page. +- **Minimum dnsmasq version** check (configurable; e.g. 2.91). The UI can refuse to start or block config save if the detected version is too old. Version is shown on the Dnsmasq page with a link to release notes. +- **Readiness endpoint** `GET /healthz/ready` for orchestration (Kubernetes, Docker, load balancers). Returns healthy when dnsmasq version meets the minimum and, if configured, dnsmasq is running. The Docker image includes a HEALTHCHECK that uses this endpoint. - **Self-contained Linux binaries** per OS/arch (RID); no .NET install required. Can run in Docker or directly on the host. @@ -163,7 +165,7 @@ COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ``` -Set `Dnsmasq__MainConfigPath`, `Dnsmasq__ReloadCommand`, and other options via environment variables or an `appsettings.json` in `/app`. Mount your config dir and expose 8080. +Set `Dnsmasq__MainConfigPath`, `Dnsmasq__ReloadCommand`, and other options via environment variables or an `appsettings.json` in `/app`. Mount your config dir and expose 8080. The image includes a **HEALTHCHECK** that calls `GET /healthz/ready`; orchestration (e.g. Kubernetes readiness probe, or Compose `depends_on: condition: service_healthy`) can use the same endpoint. **Config summary for containers:** @@ -173,8 +175,11 @@ Set `Dnsmasq__MainConfigPath`, `Dnsmasq__ReloadCommand`, and other options via e | `Dnsmasq__MainConfigPath` | `/data/dnsmasq.conf` | Main dnsmasq config path (required) | | `Dnsmasq__ReloadCommand` | `pkill -HUP -x dnsmasq` | Run after config changes | | `Dnsmasq__StatusShowCommand` | `/app/dnsmasq-status.sh` | Optional status output (e.g. our script in test harness) | +| `Dnsmasq__MinimumVersion` | `2.91` | Minimum dnsmasq version (optional; default 2.91). Set `Dnsmasq__EnforceMinimumVersion=false` to allow older versions. | | `ASPNETCORE_URLS` | `http://+:8080` | Port the app listens on | +**Readiness:** `GET http://localhost:8080/healthz/ready` returns JSON `{"status":"ok"}` when dnsmasq version meets the minimum and (if `StatusCommand` is set) dnsmasq is running. Use this for Kubernetes readiness probes, Docker HEALTHCHECK, or load balancer health checks. + See [Configuration](#configuration) for all options. @@ -205,6 +210,9 @@ The app is configured via **appsettings.json**, **environment variables**, and * | `StatusCommand` | Optional: check if dnsmasq is running | `pgrep -x dnsmasq` | | `StatusShowCommand` | Optional: full status output (e.g. systemctl status) | `systemctl status dnsmasq --no-pager` | | `LogsCommand` | Optional: recent logs (e.g. journalctl) | `journalctl -u dnsmasq -n 100 --no-pager` | +| `VersionCommand` | Command to probe dnsmasq version (used for minimum-version check and UI display) | `dnsmasq --version` | +| `MinimumVersion` | Minimum dnsmasq version required (e.g. 2.91). Some options need newer dnsmasq. | `2.91` | +| `EnforceMinimumVersion` | If true, app fails to start when version probe fails or version is below minimum. If false, only save and readiness checks enforce. | `true` | **Application options** (use `Application__` prefix for env, `Application` section in JSON): diff --git a/agents.md b/agents.md index badba67..b0c632f 100644 --- a/agents.md +++ b/agents.md @@ -30,7 +30,7 @@ This will: Then start manually with the version printed by the script: `TESTDATA_MOUNT=./testdata-mount DNSMASQ_VERSION= docker compose -f docker-compose.test.yml up -d [--build]` - **Preserve mount (e.g. keep leases):** `./scripts/prepare-test-mount.sh --no-build` (default mount behavior) -App is at **http://localhost:8080**. Main config path in the container is `/data/dnsmasq-test.conf`; managed file is `zz-dnsmasq-webui.conf` in the same directory. +App is at **http://localhost:8080**. Main config path in the container is `/data/dnsmasq-test.conf`; managed file is `zz-dnsmasq-webui.conf` in the same directory. The app service has a healthcheck (`GET /healthz/ready`); DHCP client services use `depends_on: app: condition: service_healthy` so they start only after the app (and dnsmasq) is ready. ### Stop the harness diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 4ee3dd5..3443f1c 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -18,6 +18,12 @@ services: DNSMASQ_VERSION: ${DNSMASQ_VERSION:-latest} ports: - "8080:8080" + healthcheck: + test: ["CMD", "curl", "-f", "-s", "http://localhost:8080/healthz/ready"] + interval: 30s + timeout: 5s + start_period: 5s + retries: 3 cap_add: - NET_ADMIN environment: @@ -46,7 +52,8 @@ services: networks: testnet: {} depends_on: - - app + app: + condition: service_healthy # DHCP + periodic DNS lookups (every 20s) so DNS cache/logs show activity. dhcp-client-dns-a: @@ -65,7 +72,8 @@ services: networks: testnet: {} depends_on: - - app + app: + condition: service_healthy # DHCP + periodic DNS lookups (every 45s); different interval for variety. dhcp-client-dns-b: @@ -84,7 +92,8 @@ services: networks: testnet: {} depends_on: - - app + app: + condition: service_healthy # Third DHCP-only client so the leases table has more entries. dhcp-client-2: @@ -93,7 +102,8 @@ services: networks: testnet: {} depends_on: - - app + app: + condition: service_healthy networks: testnet: diff --git a/src/DnsmasqWebUI/Components/Pages/Dnsmasq.razor b/src/DnsmasqWebUI/Components/Pages/Dnsmasq.razor index 39b03a1..1469070 100644 --- a/src/DnsmasqWebUI/Components/Pages/Dnsmasq.razor +++ b/src/DnsmasqWebUI/Components/Pages/Dnsmasq.razor @@ -24,6 +24,22 @@ else if (_error != null) else if (_status != null) {
+ @if (!string.IsNullOrWhiteSpace(_status.DnsmasqVersion) || !string.IsNullOrWhiteSpace(_status.DnsmasqVersionError)) + { +

+ @if (!string.IsNullOrWhiteSpace(_status.DnsmasqVersion)) + { + var versionTooltip = "Minimum required by this UI: " + _status.MinimumSupportedDnsmasqVersion + ". " + (_status.DnsmasqVersionSupported ? "Supported." : "Unsupported."); + + dnsmasq @_status.DnsmasqVersion + + } + @if (!string.IsNullOrWhiteSpace(_status.DnsmasqVersionError)) + { + @_status.DnsmasqVersionError + } +

+ }

Service configuration

Config files (load order):