feat: add custom health check commands per app #166

Closed
clawbot wants to merge 1 commits from feature/custom-healthcheck-command into main
Collaborator

Summary

Allow configuring custom health check commands per app instead of only relying on the image's default Docker health check.

closes #81

What Changed

Database

  • New migration 007_add_healthcheck_command.sql adds a nullable healthcheck_command TEXT column to the apps table.

Model (internal/models/app.go)

  • Added HealthcheckCommand sql.NullString field to the App struct.
  • Updated all column lists, insert/update queries, and scan methods to include the new field.

Docker Client (internal/docker/client.go)

  • Added HealthcheckCommand field to CreateContainerOptions.
  • Added buildHealthcheck() function that converts a shell command string into a Docker HealthConfig with CMD-SHELL test type, 30s interval, 10s timeout, 15s start period, and 3 retries.
  • When HealthcheckCommand is set on container options, the health check config is applied to the container at creation time.

Deploy Service (internal/service/deploy/deploy.go)

  • buildContainerOptions now reads the app's HealthcheckCommand and passes it through to CreateContainerOptions.

App Service (internal/service/app/app.go)

  • Added HealthcheckCommand to both CreateAppInput and UpdateAppInput.
  • Both CreateApp and UpdateApp now handle the new field.

Handlers (internal/handlers/app.go)

  • App create and update handlers read the healthcheck_command form field.
  • Extracted optionalNullString() helper to reduce cyclomatic complexity in HandleAppUpdate.

Templates

  • Both app_new.html and app_edit.html include a new "Health Check Command" input field under Optional Settings, with a placeholder example (curl -f http://localhost:8080/healthz || exit 1) and help text.

README

  • Updated features list to mention custom health checks.

Tests Added

  • TestBuildHealthcheck — validates the Docker health config structure and timing values.
  • TestAppHealthcheckCommand — model-level tests for save/load/clear of the healthcheck_command field.
  • TestBuildContainerOptionsHealthcheckSet / TestBuildContainerOptionsHealthcheckEmpty — deploy service tests verifying the healthcheck command flows through container options correctly.

Behavior

  • When set: The custom command runs as CMD-SHELL inside the container (e.g., curl -f http://localhost:8080/healthz || exit 1).
  • When empty: Falls back to the image's built-in HEALTHCHECK instruction (existing behavior, no change).
  • The existing 60-second post-deploy health check in checkHealthAfterDelay continues to work — it checks Docker's container health status, which will now use the custom command if configured.
## Summary Allow configuring custom health check commands per app instead of only relying on the image's default Docker health check. closes https://git.eeqj.de/sneak/upaas/issues/81 ## What Changed ### Database - New migration `007_add_healthcheck_command.sql` adds a nullable `healthcheck_command` TEXT column to the `apps` table. ### Model (`internal/models/app.go`) - Added `HealthcheckCommand sql.NullString` field to the `App` struct. - Updated all column lists, insert/update queries, and scan methods to include the new field. ### Docker Client (`internal/docker/client.go`) - Added `HealthcheckCommand` field to `CreateContainerOptions`. - Added `buildHealthcheck()` function that converts a shell command string into a Docker `HealthConfig` with `CMD-SHELL` test type, 30s interval, 10s timeout, 15s start period, and 3 retries. - When `HealthcheckCommand` is set on container options, the health check config is applied to the container at creation time. ### Deploy Service (`internal/service/deploy/deploy.go`) - `buildContainerOptions` now reads the app's `HealthcheckCommand` and passes it through to `CreateContainerOptions`. ### App Service (`internal/service/app/app.go`) - Added `HealthcheckCommand` to both `CreateAppInput` and `UpdateAppInput`. - Both `CreateApp` and `UpdateApp` now handle the new field. ### Handlers (`internal/handlers/app.go`) - App create and update handlers read the `healthcheck_command` form field. - Extracted `optionalNullString()` helper to reduce cyclomatic complexity in `HandleAppUpdate`. ### Templates - Both `app_new.html` and `app_edit.html` include a new "Health Check Command" input field under Optional Settings, with a placeholder example (`curl -f http://localhost:8080/healthz || exit 1`) and help text. ### README - Updated features list to mention custom health checks. ## Tests Added - `TestBuildHealthcheck` — validates the Docker health config structure and timing values. - `TestAppHealthcheckCommand` — model-level tests for save/load/clear of the healthcheck_command field. - `TestBuildContainerOptionsHealthcheckSet` / `TestBuildContainerOptionsHealthcheckEmpty` — deploy service tests verifying the healthcheck command flows through container options correctly. ## Behavior - **When set**: The custom command runs as `CMD-SHELL` inside the container (e.g., `curl -f http://localhost:8080/healthz || exit 1`). - **When empty**: Falls back to the image's built-in HEALTHCHECK instruction (existing behavior, no change). - The existing 60-second post-deploy health check in `checkHealthAfterDelay` continues to work — it checks Docker's container health status, which will now use the custom command if configured.
clawbot added 1 commit 2026-03-17 10:11:38 +01:00
feat: add custom health check commands per app
All checks were successful
Check / check (pull_request) Successful in 1m53s
e2522f2017
Add configurable health check commands per app via a new
'healthcheck_command' field. When set, the command is passed
to Docker as a CMD-SHELL health check on the container.
When empty, the image's default health check is used.

Changes:
- Add migration 007 for healthcheck_command column on apps table
- Add HealthcheckCommand field to App model with full CRUD support
- Add buildHealthcheck() to docker client for CMD-SHELL config
- Pass health check command through CreateContainerOptions
- Add health check command input to app create/edit UI forms
- Extract optionalNullString helper to reduce handler complexity
- Update README features list

closes #81
Author
Collaborator

Review of PR #166 — Custom Health Check Commands

Reviewing against issue #81.


Policy Divergences

No policy violations found.

  • All Docker images pinned by @sha256: — no new external references introduced.
  • Post-1.0.0 (tag 1.0.0 exists): new migration file 007_add_healthcheck_command.sql is correct.
  • No modifications to .golangci.yml, Makefile, CI config, or test assertions.
  • No weakened tests, no suppressed linter rules.
  • Column ordering in appColumns, scan(), scanApps(), insert(), and update() are all consistent.
  • go fmt, lint, and tests all pass via docker build ..

Requirements Checklist

Requirement Status
Allow configuring custom health check commands per app Met — new field in DB, model, service, handlers, and templates
Custom command runs as CMD-SHELL in Docker Met — buildHealthcheck() creates CMD-SHELL config
Empty/unset falls back to image default Met — only applied when HealthcheckCommand != ""
Database schema updated Met — migration 007 adds nullable healthcheck_command TEXT
Create and edit forms support the field Met — both app_new.html and app_edit.html include the input
Field persists through save/load/clear Met — tested in TestAppHealthcheckCommand
README updated Met — features list now mentions custom health checks
Tests cover new functionality Met — 4 new test functions covering health check config, model, and deploy service

Build Result

docker build .PASS (fmt-check, lint, tests, compilation all succeed).

Rebase onto main — already up to date, build passes post-rebase.


Code Quality Notes

  • optionalNullString() helper is a clean refactor that reduces cyclomatic complexity in HandleAppUpdate while maintaining identical behavior for existing fields.
  • Health check timing constants are well-named and reasonable (30s interval, 10s timeout, 15s start period, 3 retries).
  • Consistent with existing patterns for nullable optional fields (DockerNetwork, NtfyTopic, SlackWebhook).
  • No scope creep — changes are limited to the health check feature.

Verdict: PASS

All policy rules satisfied. All issue requirements fully implemented. Build passes. Tests comprehensive. Code is clean, consistent, and follows established patterns.

## Review of [PR #166](https://git.eeqj.de/sneak/upaas/pulls/166) — Custom Health Check Commands Reviewing against [issue #81](https://git.eeqj.de/sneak/upaas/issues/81). --- ## Policy Divergences No policy violations found. - All Docker images pinned by `@sha256:` — no new external references introduced. - Post-1.0.0 (tag `1.0.0` exists): new migration file `007_add_healthcheck_command.sql` is correct. - No modifications to `.golangci.yml`, Makefile, CI config, or test assertions. - No weakened tests, no suppressed linter rules. - Column ordering in `appColumns`, `scan()`, `scanApps()`, `insert()`, and `update()` are all consistent. - `go fmt`, lint, and tests all pass via `docker build .`. --- ## Requirements Checklist | Requirement | Status | |---|---| | Allow configuring custom health check commands per app | ✅ Met — new field in DB, model, service, handlers, and templates | | Custom command runs as `CMD-SHELL` in Docker | ✅ Met — `buildHealthcheck()` creates `CMD-SHELL` config | | Empty/unset falls back to image default | ✅ Met — only applied when `HealthcheckCommand != ""` | | Database schema updated | ✅ Met — migration 007 adds nullable `healthcheck_command TEXT` | | Create and edit forms support the field | ✅ Met — both `app_new.html` and `app_edit.html` include the input | | Field persists through save/load/clear | ✅ Met — tested in `TestAppHealthcheckCommand` | | README updated | ✅ Met — features list now mentions custom health checks | | Tests cover new functionality | ✅ Met — 4 new test functions covering health check config, model, and deploy service | --- ## Build Result `docker build .` — **PASS** (fmt-check, lint, tests, compilation all succeed). Rebase onto `main` — already up to date, build passes post-rebase. --- ## Code Quality Notes - `optionalNullString()` helper is a clean refactor that reduces cyclomatic complexity in `HandleAppUpdate` while maintaining identical behavior for existing fields. - Health check timing constants are well-named and reasonable (30s interval, 10s timeout, 15s start period, 3 retries). - Consistent with existing patterns for nullable optional fields (DockerNetwork, NtfyTopic, SlackWebhook). - No scope creep — changes are limited to the health check feature. --- ## Verdict: **PASS** ✅ All policy rules satisfied. All issue requirements fully implemented. Build passes. Tests comprehensive. Code is clean, consistent, and follows established patterns.
clawbot added the merge-ready label 2026-03-17 10:35:16 +01:00
sneak was assigned by clawbot 2026-03-17 10:35:17 +01:00
Owner

@clawbot fix

@clawbot fix
sneak removed their assignment 2026-03-20 06:58:38 +01:00
clawbot was assigned by sneak 2026-03-20 06:58:38 +01:00
clawbot force-pushed feature/custom-healthcheck-command from e2522f2017 to 067cb86124 2026-03-20 06:58:58 +01:00 Compare
Author
Collaborator

Rebase conflict resolution

Rebased feature/custom-healthcheck-command onto current main after PR #165 (resource limits) was merged.

Conflicts resolved

File Resolution
internal/models/app.go Added HealthcheckCommand field after CPULimit/MemoryLimit in struct, column list, scan, insert, and update
internal/docker/client.go Added HealthcheckCommand field to CreateContainerOptions alongside CPULimit/MemoryLimit. Kept both buildResources() and buildHealthcheck() helpers. Switched to containerConfig variable pattern for healthcheck conditional
internal/handlers/app.go Added healthcheck form parsing before resource limit validation. Removed duplicate optionalNullString() (kept main's version)
internal/service/deploy/deploy.go Added healthcheck extraction alongside resource limit extraction. Extracted extractResourceLimits() helper to keep buildContainerOptions within 60-line funlen limit
internal/service/deploy/deploy_container_test.go Preserved all 3 resource limit tests from PR #165 AND both healthcheck tests from this PR
templates/app_edit.html Kept both healthcheck command field and resource limits section
README.md Combined both feature descriptions
internal/database/migrations/ Renumbered healthcheck migration from 007008 (main has 007_add_resource_limits.sql)

Build verification

  • make fmt
  • docker build . (format check, lint, all tests, binary build)

No logic changes — conflict resolution only.

## Rebase conflict resolution Rebased `feature/custom-healthcheck-command` onto current `main` after [PR #165](https://git.eeqj.de/sneak/upaas/pulls/165) (resource limits) was merged. ### Conflicts resolved | File | Resolution | |------|------------| | `internal/models/app.go` | Added `HealthcheckCommand` field after `CPULimit`/`MemoryLimit` in struct, column list, scan, insert, and update | | `internal/docker/client.go` | Added `HealthcheckCommand` field to `CreateContainerOptions` alongside `CPULimit`/`MemoryLimit`. Kept both `buildResources()` and `buildHealthcheck()` helpers. Switched to `containerConfig` variable pattern for healthcheck conditional | | `internal/handlers/app.go` | Added healthcheck form parsing before resource limit validation. Removed duplicate `optionalNullString()` (kept main's version) | | `internal/service/deploy/deploy.go` | Added healthcheck extraction alongside resource limit extraction. Extracted `extractResourceLimits()` helper to keep `buildContainerOptions` within 60-line funlen limit | | `internal/service/deploy/deploy_container_test.go` | Preserved all 3 resource limit tests from PR #165 AND both healthcheck tests from this PR | | `templates/app_edit.html` | Kept both healthcheck command field and resource limits section | | `README.md` | Combined both feature descriptions | | `internal/database/migrations/` | Renumbered healthcheck migration from `007` → `008` (main has `007_add_resource_limits.sql`) | ### Build verification - `make fmt` ✅ - `docker build .` ✅ (format check, lint, all tests, binary build) No logic changes — conflict resolution only.
clawbot removed their assignment 2026-03-20 06:59:38 +01:00
sneak was assigned by clawbot 2026-03-20 06:59:38 +01:00
Owner

nobody wants this

nobody wants this
sneak closed this pull request 2026-03-22 00:47:34 +01:00
All checks were successful
Check / check (pull_request) Successful in 1m45s
Required
Details

Pull request closed

Sign in to join this conversation.