mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
api/types: move container Health types to api/types/container
This moves the `Health` and `HealthcheckResult` types to the container package, as well as the related `NoHealthcheck`, `Starting`, `Healthy`, and `Unhealthy` consts. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
26
api/types/container/health.go
Normal file
26
api/types/container/health.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package container
|
||||
|
||||
import "time"
|
||||
|
||||
// Health states
|
||||
const (
|
||||
NoHealthcheck = "none" // Indicates there is no healthcheck
|
||||
Starting = "starting" // Starting indicates that the container is not yet ready
|
||||
Healthy = "healthy" // Healthy indicates that the container is running correctly
|
||||
Unhealthy = "unhealthy" // Unhealthy indicates that the container has a problem
|
||||
)
|
||||
|
||||
// Health stores information about the container's healthcheck results
|
||||
type Health struct {
|
||||
Status string // Status is one of [Starting], [Healthy] or [Unhealthy].
|
||||
FailingStreak int // FailingStreak is the number of consecutive failures
|
||||
Log []*HealthcheckResult // Log contains the last few results (oldest first)
|
||||
}
|
||||
|
||||
// HealthcheckResult stores information about a single run of a healthcheck probe
|
||||
type HealthcheckResult struct {
|
||||
Start time.Time // Start is the time this check started
|
||||
End time.Time // End is the time this check ended
|
||||
ExitCode int // ExitCode meanings: 0=healthy, 1=unhealthy, 2=reserved (considered unhealthy), else=error running probe
|
||||
Output string // Output from last check
|
||||
}
|
||||
@@ -203,29 +203,6 @@ type Version struct {
|
||||
BuildTime string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// HealthcheckResult stores information about a single run of a healthcheck probe
|
||||
type HealthcheckResult struct {
|
||||
Start time.Time // Start is the time this check started
|
||||
End time.Time // End is the time this check ended
|
||||
ExitCode int // ExitCode meanings: 0=healthy, 1=unhealthy, 2=reserved (considered unhealthy), else=error running probe
|
||||
Output string // Output from last check
|
||||
}
|
||||
|
||||
// Health states
|
||||
const (
|
||||
NoHealthcheck = "none" // Indicates there is no healthcheck
|
||||
Starting = "starting" // Starting indicates that the container is not yet ready
|
||||
Healthy = "healthy" // Healthy indicates that the container is running correctly
|
||||
Unhealthy = "unhealthy" // Unhealthy indicates that the container has a problem
|
||||
)
|
||||
|
||||
// Health stores information about the container's healthcheck results
|
||||
type Health struct {
|
||||
Status string // Status is one of Starting, Healthy or Unhealthy
|
||||
FailingStreak int // FailingStreak is the number of consecutive failures
|
||||
Log []*HealthcheckResult // Log contains the last few results (oldest first)
|
||||
}
|
||||
|
||||
// ContainerState stores container's running state
|
||||
// it's part of ContainerJSONBase and will return by "inspect" command
|
||||
type ContainerState struct {
|
||||
@@ -240,7 +217,7 @@ type ContainerState struct {
|
||||
Error string
|
||||
StartedAt string
|
||||
FinishedAt string
|
||||
Health *Health `json:",omitempty"`
|
||||
Health *container.Health `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ContainerJSONBase contains response of Engine API:
|
||||
|
||||
@@ -231,3 +231,21 @@ type DefaultNetworkSettings = container.DefaultNetworkSettings
|
||||
//
|
||||
// Deprecated: use [container.NetworkSettingsSummary].
|
||||
type SummaryNetworkSettings = container.NetworkSettingsSummary
|
||||
|
||||
// Health states
|
||||
const (
|
||||
NoHealthcheck = container.NoHealthcheck // Deprecated: use [container.NoHealthcheck].
|
||||
Starting = container.Starting // Deprecated: use [container.Starting].
|
||||
Healthy = container.Healthy // Deprecated: use [container.Healthy].
|
||||
Unhealthy = container.Unhealthy // Deprecated: use [container.Unhealthy].
|
||||
)
|
||||
|
||||
// Health stores information about the container's healthcheck results.
|
||||
//
|
||||
// Deprecated: use [container.Health].
|
||||
type Health = container.Health
|
||||
|
||||
// HealthcheckResult stores information about a single run of a healthcheck probe.
|
||||
//
|
||||
// Deprecated: use [container.HealthcheckResult].
|
||||
type HealthcheckResult = container.HealthcheckResult
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
)
|
||||
|
||||
// Health holds the current container health-check state
|
||||
type Health struct {
|
||||
types.Health
|
||||
container.Health
|
||||
stop chan struct{} // Write struct{} to stop the monitor
|
||||
mu sync.Mutex
|
||||
}
|
||||
@@ -20,7 +20,7 @@ func (s *Health) String() string {
|
||||
status := s.Status()
|
||||
|
||||
switch status {
|
||||
case types.Starting:
|
||||
case container.Starting:
|
||||
return "health: starting"
|
||||
default: // Healthy and Unhealthy are clear on their own
|
||||
return status
|
||||
@@ -36,7 +36,7 @@ func (s *Health) Status() string {
|
||||
|
||||
// This happens when the monitor has yet to be setup.
|
||||
if s.Health.Status == "" {
|
||||
return types.Unhealthy
|
||||
return container.Unhealthy
|
||||
}
|
||||
|
||||
return s.Health.Status
|
||||
@@ -77,7 +77,7 @@ func (s *Health) CloseMonitorChannel() {
|
||||
close(s.stop)
|
||||
s.stop = nil
|
||||
// unhealthy when the monitor has stopped for compatibility reasons
|
||||
s.Health.Status = types.Unhealthy
|
||||
s.Health.Status = container.Unhealthy
|
||||
log.G(context.TODO()).Debug("CloseMonitorChannel done")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
@@ -110,10 +110,10 @@ func (s *State) String() string {
|
||||
|
||||
// IsValidHealthString checks if the provided string is a valid container health status or not.
|
||||
func IsValidHealthString(s string) bool {
|
||||
return s == types.Starting ||
|
||||
s == types.Healthy ||
|
||||
s == types.Unhealthy ||
|
||||
s == types.NoHealthcheck
|
||||
return s == container.Starting ||
|
||||
s == container.Healthy ||
|
||||
s == container.Unhealthy ||
|
||||
s == container.NoHealthcheck
|
||||
}
|
||||
|
||||
// StateString returns a single string to describe state
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
|
||||
)
|
||||
|
||||
@@ -14,10 +14,10 @@ func TestIsValidHealthString(t *testing.T) {
|
||||
Health string
|
||||
Expected bool
|
||||
}{
|
||||
{types.Healthy, true},
|
||||
{types.Unhealthy, true},
|
||||
{types.Starting, true},
|
||||
{types.NoHealthcheck, true},
|
||||
{container.Healthy, true},
|
||||
{container.Unhealthy, true},
|
||||
{container.Starting, true},
|
||||
{container.NoHealthcheck, true},
|
||||
{"fail", false},
|
||||
}
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ func (v *View) GetAllNames() map[string][]string {
|
||||
// transform maps a (deep) copied Container object to what queries need.
|
||||
// A lock on the Container is not held because these are immutable deep copies.
|
||||
func (v *View) transform(ctr *Container) *Snapshot {
|
||||
health := types.NoHealthcheck
|
||||
health := container.NoHealthcheck
|
||||
if ctr.Health != nil {
|
||||
health = ctr.Health.Status()
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/google/uuid"
|
||||
"gotest.tools/v3/assert"
|
||||
@@ -37,7 +36,7 @@ func newContainer(t *testing.T) *Container {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c := NewBaseContainer(id, cRoot)
|
||||
c.HostConfig = &containertypes.HostConfig{}
|
||||
c.HostConfig = &container.HostConfig{}
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -171,7 +170,7 @@ func TestViewWithHealthCheck(t *testing.T) {
|
||||
one = newContainer(t)
|
||||
)
|
||||
one.Health = &Health{
|
||||
Health: types.Health{
|
||||
Health: container.Health{
|
||||
Status: "starting",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/docker/docker/container"
|
||||
@@ -55,7 +55,7 @@ const (
|
||||
type probe interface {
|
||||
// Perform one run of the check. Returns the exit code and an optional
|
||||
// short diagnostic string.
|
||||
run(context.Context, *Daemon, *container.Container) (*types.HealthcheckResult, error)
|
||||
run(context.Context, *Daemon, *container.Container) (*containertypes.HealthcheckResult, error)
|
||||
}
|
||||
|
||||
// cmdProbe implements the "CMD" probe type.
|
||||
@@ -66,7 +66,7 @@ type cmdProbe struct {
|
||||
|
||||
// exec the healthcheck command in the container.
|
||||
// Returns the exit code and probe output (if any)
|
||||
func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container) (*types.HealthcheckResult, error) {
|
||||
func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container) (*containertypes.HealthcheckResult, error) {
|
||||
startTime := time.Now()
|
||||
cmdSlice := strslice.StrSlice(cntr.Config.Healthcheck.Test)[1:]
|
||||
if p.shell {
|
||||
@@ -145,7 +145,7 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container
|
||||
} else {
|
||||
msg = fmt.Sprintf("Health check exceeded timeout (%v)", probeTimeout)
|
||||
}
|
||||
return &types.HealthcheckResult{
|
||||
return &containertypes.HealthcheckResult{
|
||||
ExitCode: -1,
|
||||
Output: msg,
|
||||
End: time.Now(),
|
||||
@@ -173,7 +173,7 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container
|
||||
}
|
||||
// Note: Go's json package will handle invalid UTF-8 for us
|
||||
out := output.String()
|
||||
return &types.HealthcheckResult{
|
||||
return &containertypes.HealthcheckResult{
|
||||
End: time.Now(),
|
||||
ExitCode: exitCode,
|
||||
Output: out,
|
||||
@@ -181,7 +181,7 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container
|
||||
}
|
||||
|
||||
// Update the container's Status.Health struct based on the latest probe's result.
|
||||
func handleProbeResult(d *Daemon, c *container.Container, result *types.HealthcheckResult, done chan struct{}) {
|
||||
func handleProbeResult(d *Daemon, c *container.Container, result *containertypes.HealthcheckResult, done chan struct{}) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
@@ -208,14 +208,14 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch
|
||||
|
||||
if result.ExitCode == exitStatusHealthy {
|
||||
h.FailingStreak = 0
|
||||
h.SetStatus(types.Healthy)
|
||||
h.SetStatus(containertypes.Healthy)
|
||||
} else { // Failure (including invalid exit code)
|
||||
shouldIncrementStreak := true
|
||||
|
||||
// If the container is starting (i.e. we never had a successful health check)
|
||||
// then we check if we are within the start period of the container in which
|
||||
// case we do not increment the failure streak.
|
||||
if h.Status() == types.Starting {
|
||||
if h.Status() == containertypes.Starting {
|
||||
startPeriod := timeoutWithDefault(c.Config.Healthcheck.StartPeriod, defaultStartPeriod)
|
||||
timeSinceStart := result.Start.Sub(c.State.StartedAt)
|
||||
|
||||
@@ -229,7 +229,7 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch
|
||||
h.FailingStreak++
|
||||
|
||||
if h.FailingStreak >= retries {
|
||||
h.SetStatus(types.Unhealthy)
|
||||
h.SetStatus(containertypes.Unhealthy)
|
||||
}
|
||||
}
|
||||
// Else we're starting or healthy. Stay in that state.
|
||||
@@ -270,7 +270,7 @@ func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe)
|
||||
status := c.Health.Health.Status
|
||||
c.Unlock()
|
||||
|
||||
if status == types.Starting {
|
||||
if status == containertypes.Starting {
|
||||
return startInterval
|
||||
}
|
||||
return probeInterval
|
||||
@@ -288,14 +288,14 @@ func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe)
|
||||
log.G(context.TODO()).Debugf("Running health check for container %s ...", c.ID)
|
||||
startTime := time.Now()
|
||||
ctx, cancelProbe := context.WithCancel(context.Background())
|
||||
results := make(chan *types.HealthcheckResult, 1)
|
||||
results := make(chan *containertypes.HealthcheckResult, 1)
|
||||
go func() {
|
||||
healthChecksCounter.Inc()
|
||||
result, err := probe.run(ctx, d, c)
|
||||
if err != nil {
|
||||
healthChecksFailedCounter.Inc()
|
||||
log.G(ctx).Warnf("Health check for container %s error: %v", c.ID, err)
|
||||
results <- &types.HealthcheckResult{
|
||||
results <- &containertypes.HealthcheckResult{
|
||||
ExitCode: -1,
|
||||
Output: err.Error(),
|
||||
Start: startTime,
|
||||
@@ -379,11 +379,11 @@ func (daemon *Daemon) initHealthMonitor(c *container.Container) {
|
||||
daemon.stopHealthchecks(c)
|
||||
|
||||
if h := c.State.Health; h != nil {
|
||||
h.SetStatus(types.Starting)
|
||||
h.SetStatus(containertypes.Starting)
|
||||
h.FailingStreak = 0
|
||||
} else {
|
||||
h := &container.Health{}
|
||||
h.SetStatus(types.Starting)
|
||||
h.SetStatus(containertypes.Starting)
|
||||
c.State.Health = h
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
eventtypes "github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/container"
|
||||
@@ -14,7 +13,7 @@ import (
|
||||
func reset(c *container.Container) {
|
||||
c.State = &container.State{}
|
||||
c.State.Health = &container.Health{}
|
||||
c.State.Health.SetStatus(types.Starting)
|
||||
c.State.Health.SetStatus(containertypes.Starting)
|
||||
}
|
||||
|
||||
func TestNoneHealthcheck(t *testing.T) {
|
||||
@@ -87,7 +86,7 @@ func TestHealthStates(t *testing.T) {
|
||||
reset(c)
|
||||
|
||||
handleResult := func(startTime time.Time, exitCode int) {
|
||||
handleProbeResult(daemon, c, &types.HealthcheckResult{
|
||||
handleProbeResult(daemon, c, &containertypes.HealthcheckResult{
|
||||
Start: startTime,
|
||||
End: startTime,
|
||||
ExitCode: exitCode,
|
||||
@@ -112,7 +111,7 @@ func TestHealthStates(t *testing.T) {
|
||||
|
||||
handleResult(c.State.StartedAt.Add(20*time.Second), 1)
|
||||
handleResult(c.State.StartedAt.Add(40*time.Second), 1)
|
||||
if status := c.State.Health.Status(); status != types.Starting {
|
||||
if status := c.State.Health.Status(); status != containertypes.Starting {
|
||||
t.Errorf("Expecting starting, but got %#v\n", status)
|
||||
}
|
||||
if c.State.Health.FailingStreak != 2 {
|
||||
@@ -134,14 +133,14 @@ func TestHealthStates(t *testing.T) {
|
||||
c.Config.Healthcheck.StartPeriod = 30 * time.Second
|
||||
|
||||
handleResult(c.State.StartedAt.Add(20*time.Second), 1)
|
||||
if status := c.State.Health.Status(); status != types.Starting {
|
||||
if status := c.State.Health.Status(); status != containertypes.Starting {
|
||||
t.Errorf("Expecting starting, but got %#v\n", status)
|
||||
}
|
||||
if c.State.Health.FailingStreak != 0 {
|
||||
t.Errorf("Expecting FailingStreak=0, but got %d\n", c.State.Health.FailingStreak)
|
||||
}
|
||||
handleResult(c.State.StartedAt.Add(50*time.Second), 1)
|
||||
if status := c.State.Health.Status(); status != types.Starting {
|
||||
if status := c.State.Health.Status(); status != containertypes.Starting {
|
||||
t.Errorf("Expecting starting, but got %#v\n", status)
|
||||
}
|
||||
if c.State.Health.FailingStreak != 1 {
|
||||
|
||||
@@ -137,12 +137,12 @@ func (daemon *Daemon) getInspectData(daemonCfg *config.Config, container *contai
|
||||
}
|
||||
}
|
||||
|
||||
var containerHealth *types.Health
|
||||
var containerHealth *containertypes.Health
|
||||
if container.State.Health != nil {
|
||||
containerHealth = &types.Health{
|
||||
containerHealth = &containertypes.Health{
|
||||
Status: container.State.Health.Status(),
|
||||
FailingStreak: container.State.Health.FailingStreak,
|
||||
Log: append([]*types.HealthcheckResult{}, container.State.Health.Log...),
|
||||
Log: append([]*containertypes.HealthcheckResult{}, container.State.Health.Log...),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/cli/build"
|
||||
"gotest.tools/v3/assert"
|
||||
@@ -42,9 +42,9 @@ func waitForHealthStatus(c *testing.T, name string, prev string, expected string
|
||||
}
|
||||
}
|
||||
|
||||
func getHealth(c *testing.T, name string) *types.Health {
|
||||
func getHealth(c *testing.T, name string) *container.Health {
|
||||
out := cli.DockerCmd(c, "inspect", "--format={{json .State.Health}}", name).Stdout()
|
||||
var health types.Health
|
||||
var health container.Health
|
||||
err := json.Unmarshal([]byte(out), &health)
|
||||
assert.Equal(c, err, nil)
|
||||
return &health
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
@@ -30,7 +29,7 @@ func TestHealthCheckWorkdir(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, cID, types.Healthy), poll.WithDelay(100*time.Millisecond))
|
||||
poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, cID, containertypes.Healthy), poll.WithDelay(100*time.Millisecond))
|
||||
}
|
||||
|
||||
// GitHub #37263
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
@@ -111,7 +110,7 @@ func TestDaemonRestartKillContainers(t *testing.T) {
|
||||
err = apiClient.ContainerStart(ctx, resp.ID, container.StartOptions{})
|
||||
assert.NilError(t, err)
|
||||
if tc.xHealthCheck {
|
||||
poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, resp.ID, types.Healthy), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
|
||||
poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, resp.ID, container.Healthy), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
|
||||
testContainer.ExecT(ctx, t, apiClient, resp.ID, []string{"touch", "/tmp/unhealthy"}).AssertSuccess(t)
|
||||
}
|
||||
}
|
||||
@@ -132,11 +131,11 @@ func TestDaemonRestartKillContainers(t *testing.T) {
|
||||
// to become healthy, which gives us the entire StartPeriod (60s) to assert that
|
||||
// the container's health state is Starting before we have to worry about racing
|
||||
// the health monitor.
|
||||
assert.Equal(t, testContainer.Inspect(ctx, t, apiClient, resp.ID).State.Health.Status, types.Starting)
|
||||
assert.Equal(t, testContainer.Inspect(ctx, t, apiClient, resp.ID).State.Health.Status, container.Starting)
|
||||
poll.WaitOn(t, pollForNewHealthCheck(ctx, apiClient, startTime, resp.ID), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
|
||||
|
||||
testContainer.ExecT(ctx, t, apiClient, resp.ID, []string{"rm", "/tmp/unhealthy"}).AssertSuccess(t)
|
||||
poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, resp.ID, types.Healthy), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
|
||||
poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, resp.ID, container.Healthy), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
|
||||
}
|
||||
// TODO(cpuguy83): test pause states... this seems to be rather undefined currently
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user