feat: add custom health check commands per app
All checks were successful
Check / check (pull_request) Successful in 1m53s
All checks were successful
Check / check (pull_request) Successful in 1m53s
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
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
dockertypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
@@ -138,13 +139,14 @@ func (c *Client) BuildImage(
|
||||
|
||||
// CreateContainerOptions contains options for creating a container.
|
||||
type CreateContainerOptions struct {
|
||||
Name string
|
||||
Image string
|
||||
Env map[string]string
|
||||
Labels map[string]string
|
||||
Volumes []VolumeMount
|
||||
Ports []PortMapping
|
||||
Network string
|
||||
Name string
|
||||
Image string
|
||||
Env map[string]string
|
||||
Labels map[string]string
|
||||
Volumes []VolumeMount
|
||||
Ports []PortMapping
|
||||
Network string
|
||||
HealthcheckCommand string // Custom health check shell command (empty = use image default)
|
||||
}
|
||||
|
||||
// VolumeMount represents a volume mount.
|
||||
@@ -185,6 +187,29 @@ func buildPortConfig(ports []PortMapping) (nat.PortSet, nat.PortMap) {
|
||||
return exposedPorts, portBindings
|
||||
}
|
||||
|
||||
// healthcheckInterval is the time between health check attempts.
|
||||
const healthcheckInterval = 30 * time.Second
|
||||
|
||||
// healthcheckTimeout is the maximum time a single health check can take.
|
||||
const healthcheckTimeout = 10 * time.Second
|
||||
|
||||
// healthcheckStartPeriod is the grace period before health checks start counting failures.
|
||||
const healthcheckStartPeriod = 15 * time.Second
|
||||
|
||||
// healthcheckRetries is the number of consecutive failures needed to mark unhealthy.
|
||||
const healthcheckRetries = 3
|
||||
|
||||
// buildHealthcheck creates a Docker health check config from a shell command string.
|
||||
func buildHealthcheck(command string) *container.HealthConfig {
|
||||
return &container.HealthConfig{
|
||||
Test: []string{"CMD-SHELL", command},
|
||||
Interval: healthcheckInterval,
|
||||
Timeout: healthcheckTimeout,
|
||||
StartPeriod: healthcheckStartPeriod,
|
||||
Retries: healthcheckRetries,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateContainer creates a new container.
|
||||
func (c *Client) CreateContainer(
|
||||
ctx context.Context,
|
||||
@@ -218,14 +243,22 @@ func (c *Client) CreateContainer(
|
||||
// Convert ports to exposed ports and port bindings
|
||||
exposedPorts, portBindings := buildPortConfig(opts.Ports)
|
||||
|
||||
// Build container config
|
||||
containerConfig := &container.Config{
|
||||
Image: opts.Image,
|
||||
Env: envSlice,
|
||||
Labels: opts.Labels,
|
||||
ExposedPorts: exposedPorts,
|
||||
}
|
||||
|
||||
// Apply custom health check if configured
|
||||
if opts.HealthcheckCommand != "" {
|
||||
containerConfig.Healthcheck = buildHealthcheck(opts.HealthcheckCommand)
|
||||
}
|
||||
|
||||
// Create container
|
||||
resp, err := c.docker.ContainerCreate(ctx,
|
||||
&container.Config{
|
||||
Image: opts.Image,
|
||||
Env: envSlice,
|
||||
Labels: opts.Labels,
|
||||
ExposedPorts: exposedPorts,
|
||||
},
|
||||
containerConfig,
|
||||
&container.HostConfig{
|
||||
Mounts: mounts,
|
||||
PortBindings: portBindings,
|
||||
|
||||
Reference in New Issue
Block a user