mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
feat: add support for healthcheck in Compose
This commit is contained in:
+46
-16
@@ -7,6 +7,7 @@ import (
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/distribution/reference"
|
||||
@@ -74,14 +75,6 @@ type ServiceSpec struct {
|
||||
Configs []ConfigSpec
|
||||
}
|
||||
|
||||
// UpdateConfig configures how a service is updated during a deployment.
|
||||
type UpdateConfig struct {
|
||||
// Order specifies the order of operations during an update.
|
||||
// Valid values are "start-first" (default for stateless services) and "stop-first" (default for services with volumes).
|
||||
// Empty value means the strategy will determine the order based on service characteristics.
|
||||
Order string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// CaddyConfig returns the Caddy reverse proxy configuration for the service or an empty string if it's not defined.
|
||||
func (s *ServiceSpec) CaddyConfig() string {
|
||||
if s.Caddy == nil {
|
||||
@@ -251,8 +244,11 @@ type ContainerSpec struct {
|
||||
// Entrypoint overrides the default ENTRYPOINT of the image.
|
||||
Entrypoint []string
|
||||
// Env defines the environment variables to set inside the container.
|
||||
Env EnvVars
|
||||
Image string
|
||||
Env EnvVars
|
||||
// Healthcheck defines the health check configuration for the container or overrides the health check options
|
||||
// defined in the image. If nil, the image's default health check is used.
|
||||
Healthcheck *HealthcheckSpec `json:",omitempty"`
|
||||
Image string
|
||||
// Run a custom init inside the container. If nil, use the daemon's configured settings.
|
||||
Init *bool
|
||||
// LogDriver overrides the default logging driver for the container. Each Docker daemon can have its own default.
|
||||
@@ -347,6 +343,17 @@ func (s *ContainerSpec) Clone() ContainerSpec {
|
||||
spec.Entrypoint = make([]string, len(s.Entrypoint))
|
||||
copy(spec.Entrypoint, s.Entrypoint)
|
||||
}
|
||||
if s.Env != nil {
|
||||
spec.Env = make(EnvVars, len(s.Env))
|
||||
for k, v := range s.Env {
|
||||
spec.Env[k] = v
|
||||
}
|
||||
}
|
||||
if s.Healthcheck != nil {
|
||||
hc := *s.Healthcheck
|
||||
hc.Test = slices.Clone(s.Healthcheck.Test)
|
||||
spec.Healthcheck = &hc
|
||||
}
|
||||
if s.LogDriver != nil {
|
||||
logDriver := *s.LogDriver
|
||||
if s.LogDriver.Options != nil {
|
||||
@@ -354,12 +361,6 @@ func (s *ContainerSpec) Clone() ContainerSpec {
|
||||
}
|
||||
spec.LogDriver = &logDriver
|
||||
}
|
||||
if s.Env != nil {
|
||||
spec.Env = make(EnvVars, len(s.Env))
|
||||
for k, v := range s.Env {
|
||||
spec.Env[k] = v
|
||||
}
|
||||
}
|
||||
if s.Volumes != nil {
|
||||
spec.Volumes = make([]string, len(s.Volumes))
|
||||
copy(spec.Volumes, s.Volumes)
|
||||
@@ -389,6 +390,7 @@ func (s *ContainerSpec) Clone() ContainerSpec {
|
||||
if s.Resources.DeviceReservations != nil {
|
||||
spec.Resources.DeviceReservations = slices.Clone(s.Resources.DeviceReservations)
|
||||
}
|
||||
|
||||
return spec
|
||||
}
|
||||
|
||||
@@ -406,6 +408,26 @@ func (e EnvVars) ToSlice() []string {
|
||||
return env
|
||||
}
|
||||
|
||||
// HealthcheckSpec defines the health check configuration for a container.
|
||||
type HealthcheckSpec struct {
|
||||
// Test is the command used to check health.
|
||||
// Formats: ["CMD", args...], ["CMD-SHELL", "command"], or ["NONE"] to disable.
|
||||
Test []string `json:",omitempty"`
|
||||
// Interval is the time between health checks.
|
||||
// Zero means to inherit the value from the image or use the Docker default (30s) if not defined in the image.
|
||||
Interval time.Duration `json:",omitempty"`
|
||||
// Timeout is how long to wait before considering the checck to have hung.
|
||||
Timeout time.Duration `json:",omitempty"`
|
||||
// StartPeriod is the initialisation time for a container before the retries start to count down.
|
||||
StartPeriod time.Duration `json:",omitempty"`
|
||||
// StartInterval is the time between health checks during the start period.
|
||||
StartInterval time.Duration `json:",omitempty"`
|
||||
// Retries is the number of consecutive failures needed to consider a container unhealthy.
|
||||
Retries uint `json:",omitempty"`
|
||||
// Disable disables the health check defined in the image. true is equivalent to setting Test to ["NONE"].
|
||||
Disable bool `json:",omitempty"`
|
||||
}
|
||||
|
||||
type LogDriver struct {
|
||||
// Name of the logging driver to use.
|
||||
Name string
|
||||
@@ -413,6 +435,14 @@ type LogDriver struct {
|
||||
Options map[string]string
|
||||
}
|
||||
|
||||
// UpdateConfig configures how a service is updated during a deployment.
|
||||
type UpdateConfig struct {
|
||||
// Order specifies the order of operations during an update.
|
||||
// Valid values are "start-first" (default for stateless services) and "stop-first" (default for services with volumes).
|
||||
// Empty value means the strategy will determine the order based on service characteristics.
|
||||
Order string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type RunServiceResponse struct {
|
||||
ID string
|
||||
Name string
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"maps"
|
||||
"os"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
@@ -44,18 +45,19 @@ func ServiceSpecFromCompose(project *types.Project, serviceName string) (api.Ser
|
||||
|
||||
spec := api.ServiceSpec{
|
||||
Container: api.ContainerSpec{
|
||||
CapAdd: service.CapAdd,
|
||||
CapDrop: service.CapDrop,
|
||||
Command: service.Command,
|
||||
Entrypoint: service.Entrypoint,
|
||||
Env: env,
|
||||
Image: service.Image,
|
||||
Init: service.Init,
|
||||
Privileged: service.Privileged,
|
||||
PullPolicy: pullPolicy,
|
||||
Resources: resourcesFromCompose(service),
|
||||
Sysctls: service.Sysctls,
|
||||
User: service.User,
|
||||
CapAdd: service.CapAdd,
|
||||
CapDrop: service.CapDrop,
|
||||
Command: service.Command,
|
||||
Entrypoint: service.Entrypoint,
|
||||
Env: env,
|
||||
Healthcheck: healthcheckFromCompose(service.HealthCheck),
|
||||
Image: service.Image,
|
||||
Init: service.Init,
|
||||
Privileged: service.Privileged,
|
||||
PullPolicy: pullPolicy,
|
||||
Resources: resourcesFromCompose(service),
|
||||
Sysctls: service.Sysctls,
|
||||
User: service.User,
|
||||
},
|
||||
Name: serviceName,
|
||||
Mode: api.ServiceModeReplicated,
|
||||
@@ -135,6 +137,34 @@ func ServiceSpecFromCompose(project *types.Project, serviceName string) (api.Ser
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func healthcheckFromCompose(hc *types.HealthCheckConfig) *api.HealthcheckSpec {
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
if hc.Disable {
|
||||
return &api.HealthcheckSpec{Disable: true}
|
||||
}
|
||||
|
||||
spec := &api.HealthcheckSpec{Test: hc.Test}
|
||||
if hc.Interval != nil {
|
||||
spec.Interval = time.Duration(*hc.Interval)
|
||||
}
|
||||
if hc.Timeout != nil {
|
||||
spec.Timeout = time.Duration(*hc.Timeout)
|
||||
}
|
||||
if hc.StartPeriod != nil {
|
||||
spec.StartPeriod = time.Duration(*hc.StartPeriod)
|
||||
}
|
||||
if hc.StartInterval != nil {
|
||||
spec.StartInterval = time.Duration(*hc.StartInterval)
|
||||
}
|
||||
if hc.Retries != nil {
|
||||
spec.Retries = uint(*hc.Retries)
|
||||
}
|
||||
|
||||
return spec
|
||||
}
|
||||
|
||||
func resourcesFromCompose(service types.ServiceConfig) api.ContainerResources {
|
||||
resources := api.ContainerResources{
|
||||
CPU: int64(service.CPUS * 1e9),
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
composecli "github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
@@ -109,6 +110,14 @@ func TestServiceSpecFromCompose(t *testing.T) {
|
||||
"EMPTY": "",
|
||||
"VAR": "value",
|
||||
},
|
||||
Healthcheck: &api.HealthcheckSpec{
|
||||
Test: []string{"CMD", "curl", "-f", "http://localhost"},
|
||||
Interval: 1*time.Minute + 30*time.Second,
|
||||
Timeout: 10 * time.Second,
|
||||
Retries: 5,
|
||||
StartPeriod: 15 * time.Second,
|
||||
StartInterval: 2 * time.Second,
|
||||
},
|
||||
Image: "nginx:latest",
|
||||
Init: &initTrue,
|
||||
LogDriver: &api.LogDriver{
|
||||
|
||||
@@ -11,6 +11,13 @@ services:
|
||||
BOOL: "true"
|
||||
EMPTY: ""
|
||||
VAR: value
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 1m30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 15s
|
||||
start_interval: 2s
|
||||
image: nginx:latest
|
||||
init: true
|
||||
logging:
|
||||
|
||||
Reference in New Issue
Block a user