Browse code

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>

Sebastiaan van Stijn authored on 2024/06/26 02:34:29
Showing 14 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,26 @@
0
+package container
1
+
2
+import "time"
3
+
4
+// Health states
5
+const (
6
+	NoHealthcheck = "none"      // Indicates there is no healthcheck
7
+	Starting      = "starting"  // Starting indicates that the container is not yet ready
8
+	Healthy       = "healthy"   // Healthy indicates that the container is running correctly
9
+	Unhealthy     = "unhealthy" // Unhealthy indicates that the container has a problem
10
+)
11
+
12
+// Health stores information about the container's healthcheck results
13
+type Health struct {
14
+	Status        string               // Status is one of [Starting], [Healthy] or [Unhealthy].
15
+	FailingStreak int                  // FailingStreak is the number of consecutive failures
16
+	Log           []*HealthcheckResult // Log contains the last few results (oldest first)
17
+}
18
+
19
+// HealthcheckResult stores information about a single run of a healthcheck probe
20
+type HealthcheckResult struct {
21
+	Start    time.Time // Start is the time this check started
22
+	End      time.Time // End is the time this check ended
23
+	ExitCode int       // ExitCode meanings: 0=healthy, 1=unhealthy, 2=reserved (considered unhealthy), else=error running probe
24
+	Output   string    // Output from last check
25
+}
... ...
@@ -203,29 +203,6 @@ type Version struct {
203 203
 	BuildTime     string `json:",omitempty"`
204 204
 }
205 205
 
206
-// HealthcheckResult stores information about a single run of a healthcheck probe
207
-type HealthcheckResult struct {
208
-	Start    time.Time // Start is the time this check started
209
-	End      time.Time // End is the time this check ended
210
-	ExitCode int       // ExitCode meanings: 0=healthy, 1=unhealthy, 2=reserved (considered unhealthy), else=error running probe
211
-	Output   string    // Output from last check
212
-}
213
-
214
-// Health states
215
-const (
216
-	NoHealthcheck = "none"      // Indicates there is no healthcheck
217
-	Starting      = "starting"  // Starting indicates that the container is not yet ready
218
-	Healthy       = "healthy"   // Healthy indicates that the container is running correctly
219
-	Unhealthy     = "unhealthy" // Unhealthy indicates that the container has a problem
220
-)
221
-
222
-// Health stores information about the container's healthcheck results
223
-type Health struct {
224
-	Status        string               // Status is one of Starting, Healthy or Unhealthy
225
-	FailingStreak int                  // FailingStreak is the number of consecutive failures
226
-	Log           []*HealthcheckResult // Log contains the last few results (oldest first)
227
-}
228
-
229 206
 // ContainerState stores container's running state
230 207
 // it's part of ContainerJSONBase and will return by "inspect" command
231 208
 type ContainerState struct {
... ...
@@ -240,7 +217,7 @@ type ContainerState struct {
240 240
 	Error      string
241 241
 	StartedAt  string
242 242
 	FinishedAt string
243
-	Health     *Health `json:",omitempty"`
243
+	Health     *container.Health `json:",omitempty"`
244 244
 }
245 245
 
246 246
 // ContainerJSONBase contains response of Engine API:
... ...
@@ -231,3 +231,21 @@ type DefaultNetworkSettings = container.DefaultNetworkSettings
231 231
 //
232 232
 // Deprecated: use [container.NetworkSettingsSummary].
233 233
 type SummaryNetworkSettings = container.NetworkSettingsSummary
234
+
235
+// Health states
236
+const (
237
+	NoHealthcheck = container.NoHealthcheck // Deprecated: use [container.NoHealthcheck].
238
+	Starting      = container.Starting      // Deprecated: use [container.Starting].
239
+	Healthy       = container.Healthy       // Deprecated: use [container.Healthy].
240
+	Unhealthy     = container.Unhealthy     // Deprecated: use [container.Unhealthy].
241
+)
242
+
243
+// Health stores information about the container's healthcheck results.
244
+//
245
+// Deprecated: use [container.Health].
246
+type Health = container.Health
247
+
248
+// HealthcheckResult stores information about a single run of a healthcheck probe.
249
+//
250
+// Deprecated: use [container.HealthcheckResult].
251
+type HealthcheckResult = container.HealthcheckResult
... ...
@@ -5,12 +5,12 @@ import (
5 5
 	"sync"
6 6
 
7 7
 	"github.com/containerd/log"
8
-	"github.com/docker/docker/api/types"
8
+	"github.com/docker/docker/api/types/container"
9 9
 )
10 10
 
11 11
 // Health holds the current container health-check state
12 12
 type Health struct {
13
-	types.Health
13
+	container.Health
14 14
 	stop chan struct{} // Write struct{} to stop the monitor
15 15
 	mu   sync.Mutex
16 16
 }
... ...
@@ -20,7 +20,7 @@ func (s *Health) String() string {
20 20
 	status := s.Status()
21 21
 
22 22
 	switch status {
23
-	case types.Starting:
23
+	case container.Starting:
24 24
 		return "health: starting"
25 25
 	default: // Healthy and Unhealthy are clear on their own
26 26
 		return status
... ...
@@ -36,7 +36,7 @@ func (s *Health) Status() string {
36 36
 
37 37
 	// This happens when the monitor has yet to be setup.
38 38
 	if s.Health.Status == "" {
39
-		return types.Unhealthy
39
+		return container.Unhealthy
40 40
 	}
41 41
 
42 42
 	return s.Health.Status
... ...
@@ -77,7 +77,7 @@ func (s *Health) CloseMonitorChannel() {
77 77
 		close(s.stop)
78 78
 		s.stop = nil
79 79
 		// unhealthy when the monitor has stopped for compatibility reasons
80
-		s.Health.Status = types.Unhealthy
80
+		s.Health.Status = container.Unhealthy
81 81
 		log.G(context.TODO()).Debug("CloseMonitorChannel done")
82 82
 	}
83 83
 }
... ...
@@ -7,7 +7,7 @@ import (
7 7
 	"sync"
8 8
 	"time"
9 9
 
10
-	"github.com/docker/docker/api/types"
10
+	"github.com/docker/docker/api/types/container"
11 11
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
12 12
 	units "github.com/docker/go-units"
13 13
 )
... ...
@@ -110,10 +110,10 @@ func (s *State) String() string {
110 110
 
111 111
 // IsValidHealthString checks if the provided string is a valid container health status or not.
112 112
 func IsValidHealthString(s string) bool {
113
-	return s == types.Starting ||
114
-		s == types.Healthy ||
115
-		s == types.Unhealthy ||
116
-		s == types.NoHealthcheck
113
+	return s == container.Starting ||
114
+		s == container.Healthy ||
115
+		s == container.Unhealthy ||
116
+		s == container.NoHealthcheck
117 117
 }
118 118
 
119 119
 // StateString returns a single string to describe state
... ...
@@ -5,7 +5,7 @@ import (
5 5
 	"testing"
6 6
 	"time"
7 7
 
8
-	"github.com/docker/docker/api/types"
8
+	"github.com/docker/docker/api/types/container"
9 9
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
10 10
 )
11 11
 
... ...
@@ -14,10 +14,10 @@ func TestIsValidHealthString(t *testing.T) {
14 14
 		Health   string
15 15
 		Expected bool
16 16
 	}{
17
-		{types.Healthy, true},
18
-		{types.Unhealthy, true},
19
-		{types.Starting, true},
20
-		{types.NoHealthcheck, true},
17
+		{container.Healthy, true},
18
+		{container.Unhealthy, true},
19
+		{container.Starting, true},
20
+		{container.NoHealthcheck, true},
21 21
 		{"fail", false},
22 22
 	}
23 23
 
... ...
@@ -301,7 +301,7 @@ func (v *View) GetAllNames() map[string][]string {
301 301
 // transform maps a (deep) copied Container object to what queries need.
302 302
 // A lock on the Container is not held because these are immutable deep copies.
303 303
 func (v *View) transform(ctr *Container) *Snapshot {
304
-	health := types.NoHealthcheck
304
+	health := container.NoHealthcheck
305 305
 	if ctr.Health != nil {
306 306
 		health = ctr.Health.Status()
307 307
 	}
... ...
@@ -7,8 +7,7 @@ import (
7 7
 	"path/filepath"
8 8
 	"testing"
9 9
 
10
-	"github.com/docker/docker/api/types"
11
-	containertypes "github.com/docker/docker/api/types/container"
10
+	"github.com/docker/docker/api/types/container"
12 11
 	"github.com/docker/docker/pkg/stringid"
13 12
 	"github.com/google/uuid"
14 13
 	"gotest.tools/v3/assert"
... ...
@@ -37,7 +36,7 @@ func newContainer(t *testing.T) *Container {
37 37
 		t.Fatal(err)
38 38
 	}
39 39
 	c := NewBaseContainer(id, cRoot)
40
-	c.HostConfig = &containertypes.HostConfig{}
40
+	c.HostConfig = &container.HostConfig{}
41 41
 	return c
42 42
 }
43 43
 
... ...
@@ -171,7 +170,7 @@ func TestViewWithHealthCheck(t *testing.T) {
171 171
 		one   = newContainer(t)
172 172
 	)
173 173
 	one.Health = &Health{
174
-		Health: types.Health{
174
+		Health: container.Health{
175 175
 			Status: "starting",
176 176
 		},
177 177
 	}
... ...
@@ -10,8 +10,8 @@ import (
10 10
 	"time"
11 11
 
12 12
 	"github.com/containerd/log"
13
-	"github.com/docker/docker/api/types"
14 13
 	"github.com/docker/docker/api/types/backend"
14
+	containertypes "github.com/docker/docker/api/types/container"
15 15
 	"github.com/docker/docker/api/types/events"
16 16
 	"github.com/docker/docker/api/types/strslice"
17 17
 	"github.com/docker/docker/container"
... ...
@@ -55,7 +55,7 @@ const (
55 55
 type probe interface {
56 56
 	// Perform one run of the check. Returns the exit code and an optional
57 57
 	// short diagnostic string.
58
-	run(context.Context, *Daemon, *container.Container) (*types.HealthcheckResult, error)
58
+	run(context.Context, *Daemon, *container.Container) (*containertypes.HealthcheckResult, error)
59 59
 }
60 60
 
61 61
 // cmdProbe implements the "CMD" probe type.
... ...
@@ -66,7 +66,7 @@ type cmdProbe struct {
66 66
 
67 67
 // exec the healthcheck command in the container.
68 68
 // Returns the exit code and probe output (if any)
69
-func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container) (*types.HealthcheckResult, error) {
69
+func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container) (*containertypes.HealthcheckResult, error) {
70 70
 	startTime := time.Now()
71 71
 	cmdSlice := strslice.StrSlice(cntr.Config.Healthcheck.Test)[1:]
72 72
 	if p.shell {
... ...
@@ -145,7 +145,7 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container
145 145
 		} else {
146 146
 			msg = fmt.Sprintf("Health check exceeded timeout (%v)", probeTimeout)
147 147
 		}
148
-		return &types.HealthcheckResult{
148
+		return &containertypes.HealthcheckResult{
149 149
 			ExitCode: -1,
150 150
 			Output:   msg,
151 151
 			End:      time.Now(),
... ...
@@ -173,7 +173,7 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container
173 173
 	}
174 174
 	// Note: Go's json package will handle invalid UTF-8 for us
175 175
 	out := output.String()
176
-	return &types.HealthcheckResult{
176
+	return &containertypes.HealthcheckResult{
177 177
 		End:      time.Now(),
178 178
 		ExitCode: exitCode,
179 179
 		Output:   out,
... ...
@@ -181,7 +181,7 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container
181 181
 }
182 182
 
183 183
 // Update the container's Status.Health struct based on the latest probe's result.
184
-func handleProbeResult(d *Daemon, c *container.Container, result *types.HealthcheckResult, done chan struct{}) {
184
+func handleProbeResult(d *Daemon, c *container.Container, result *containertypes.HealthcheckResult, done chan struct{}) {
185 185
 	c.Lock()
186 186
 	defer c.Unlock()
187 187
 
... ...
@@ -208,14 +208,14 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch
208 208
 
209 209
 	if result.ExitCode == exitStatusHealthy {
210 210
 		h.FailingStreak = 0
211
-		h.SetStatus(types.Healthy)
211
+		h.SetStatus(containertypes.Healthy)
212 212
 	} else { // Failure (including invalid exit code)
213 213
 		shouldIncrementStreak := true
214 214
 
215 215
 		// If the container is starting (i.e. we never had a successful health check)
216 216
 		// then we check if we are within the start period of the container in which
217 217
 		// case we do not increment the failure streak.
218
-		if h.Status() == types.Starting {
218
+		if h.Status() == containertypes.Starting {
219 219
 			startPeriod := timeoutWithDefault(c.Config.Healthcheck.StartPeriod, defaultStartPeriod)
220 220
 			timeSinceStart := result.Start.Sub(c.State.StartedAt)
221 221
 
... ...
@@ -229,7 +229,7 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch
229 229
 			h.FailingStreak++
230 230
 
231 231
 			if h.FailingStreak >= retries {
232
-				h.SetStatus(types.Unhealthy)
232
+				h.SetStatus(containertypes.Unhealthy)
233 233
 			}
234 234
 		}
235 235
 		// 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)
270 270
 		status := c.Health.Health.Status
271 271
 		c.Unlock()
272 272
 
273
-		if status == types.Starting {
273
+		if status == containertypes.Starting {
274 274
 			return startInterval
275 275
 		}
276 276
 		return probeInterval
... ...
@@ -288,14 +288,14 @@ func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe)
288 288
 			log.G(context.TODO()).Debugf("Running health check for container %s ...", c.ID)
289 289
 			startTime := time.Now()
290 290
 			ctx, cancelProbe := context.WithCancel(context.Background())
291
-			results := make(chan *types.HealthcheckResult, 1)
291
+			results := make(chan *containertypes.HealthcheckResult, 1)
292 292
 			go func() {
293 293
 				healthChecksCounter.Inc()
294 294
 				result, err := probe.run(ctx, d, c)
295 295
 				if err != nil {
296 296
 					healthChecksFailedCounter.Inc()
297 297
 					log.G(ctx).Warnf("Health check for container %s error: %v", c.ID, err)
298
-					results <- &types.HealthcheckResult{
298
+					results <- &containertypes.HealthcheckResult{
299 299
 						ExitCode: -1,
300 300
 						Output:   err.Error(),
301 301
 						Start:    startTime,
... ...
@@ -379,11 +379,11 @@ func (daemon *Daemon) initHealthMonitor(c *container.Container) {
379 379
 	daemon.stopHealthchecks(c)
380 380
 
381 381
 	if h := c.State.Health; h != nil {
382
-		h.SetStatus(types.Starting)
382
+		h.SetStatus(containertypes.Starting)
383 383
 		h.FailingStreak = 0
384 384
 	} else {
385 385
 		h := &container.Health{}
386
-		h.SetStatus(types.Starting)
386
+		h.SetStatus(containertypes.Starting)
387 387
 		c.State.Health = h
388 388
 	}
389 389
 
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"testing"
5 5
 	"time"
6 6
 
7
-	"github.com/docker/docker/api/types"
8 7
 	containertypes "github.com/docker/docker/api/types/container"
9 8
 	eventtypes "github.com/docker/docker/api/types/events"
10 9
 	"github.com/docker/docker/container"
... ...
@@ -14,7 +13,7 @@ import (
14 14
 func reset(c *container.Container) {
15 15
 	c.State = &container.State{}
16 16
 	c.State.Health = &container.Health{}
17
-	c.State.Health.SetStatus(types.Starting)
17
+	c.State.Health.SetStatus(containertypes.Starting)
18 18
 }
19 19
 
20 20
 func TestNoneHealthcheck(t *testing.T) {
... ...
@@ -87,7 +86,7 @@ func TestHealthStates(t *testing.T) {
87 87
 	reset(c)
88 88
 
89 89
 	handleResult := func(startTime time.Time, exitCode int) {
90
-		handleProbeResult(daemon, c, &types.HealthcheckResult{
90
+		handleProbeResult(daemon, c, &containertypes.HealthcheckResult{
91 91
 			Start:    startTime,
92 92
 			End:      startTime,
93 93
 			ExitCode: exitCode,
... ...
@@ -112,7 +111,7 @@ func TestHealthStates(t *testing.T) {
112 112
 
113 113
 	handleResult(c.State.StartedAt.Add(20*time.Second), 1)
114 114
 	handleResult(c.State.StartedAt.Add(40*time.Second), 1)
115
-	if status := c.State.Health.Status(); status != types.Starting {
115
+	if status := c.State.Health.Status(); status != containertypes.Starting {
116 116
 		t.Errorf("Expecting starting, but got %#v\n", status)
117 117
 	}
118 118
 	if c.State.Health.FailingStreak != 2 {
... ...
@@ -134,14 +133,14 @@ func TestHealthStates(t *testing.T) {
134 134
 	c.Config.Healthcheck.StartPeriod = 30 * time.Second
135 135
 
136 136
 	handleResult(c.State.StartedAt.Add(20*time.Second), 1)
137
-	if status := c.State.Health.Status(); status != types.Starting {
137
+	if status := c.State.Health.Status(); status != containertypes.Starting {
138 138
 		t.Errorf("Expecting starting, but got %#v\n", status)
139 139
 	}
140 140
 	if c.State.Health.FailingStreak != 0 {
141 141
 		t.Errorf("Expecting FailingStreak=0, but got %d\n", c.State.Health.FailingStreak)
142 142
 	}
143 143
 	handleResult(c.State.StartedAt.Add(50*time.Second), 1)
144
-	if status := c.State.Health.Status(); status != types.Starting {
144
+	if status := c.State.Health.Status(); status != containertypes.Starting {
145 145
 		t.Errorf("Expecting starting, but got %#v\n", status)
146 146
 	}
147 147
 	if c.State.Health.FailingStreak != 1 {
... ...
@@ -137,12 +137,12 @@ func (daemon *Daemon) getInspectData(daemonCfg *config.Config, container *contai
137 137
 		}
138 138
 	}
139 139
 
140
-	var containerHealth *types.Health
140
+	var containerHealth *containertypes.Health
141 141
 	if container.State.Health != nil {
142
-		containerHealth = &types.Health{
142
+		containerHealth = &containertypes.Health{
143 143
 			Status:        container.State.Health.Status(),
144 144
 			FailingStreak: container.State.Health.FailingStreak,
145
-			Log:           append([]*types.HealthcheckResult{}, container.State.Health.Log...),
145
+			Log:           append([]*containertypes.HealthcheckResult{}, container.State.Health.Log...),
146 146
 		}
147 147
 	}
148 148
 
... ...
@@ -8,7 +8,7 @@ import (
8 8
 	"testing"
9 9
 	"time"
10 10
 
11
-	"github.com/docker/docker/api/types"
11
+	"github.com/docker/docker/api/types/container"
12 12
 	"github.com/docker/docker/integration-cli/cli"
13 13
 	"github.com/docker/docker/integration-cli/cli/build"
14 14
 	"gotest.tools/v3/assert"
... ...
@@ -42,9 +42,9 @@ func waitForHealthStatus(c *testing.T, name string, prev string, expected string
42 42
 	}
43 43
 }
44 44
 
45
-func getHealth(c *testing.T, name string) *types.Health {
45
+func getHealth(c *testing.T, name string) *container.Health {
46 46
 	out := cli.DockerCmd(c, "inspect", "--format={{json .State.Health}}", name).Stdout()
47
-	var health types.Health
47
+	var health container.Health
48 48
 	err := json.Unmarshal([]byte(out), &health)
49 49
 	assert.Equal(c, err, nil)
50 50
 	return &health
... ...
@@ -6,7 +6,6 @@ import (
6 6
 	"testing"
7 7
 	"time"
8 8
 
9
-	"github.com/docker/docker/api/types"
10 9
 	containertypes "github.com/docker/docker/api/types/container"
11 10
 	"github.com/docker/docker/client"
12 11
 	"github.com/docker/docker/integration/internal/container"
... ...
@@ -30,7 +29,7 @@ func TestHealthCheckWorkdir(t *testing.T) {
30 30
 		}
31 31
 	})
32 32
 
33
-	poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, cID, types.Healthy), poll.WithDelay(100*time.Millisecond))
33
+	poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, cID, containertypes.Healthy), poll.WithDelay(100*time.Millisecond))
34 34
 }
35 35
 
36 36
 // GitHub #37263
... ...
@@ -7,7 +7,6 @@ import (
7 7
 	"testing"
8 8
 	"time"
9 9
 
10
-	"github.com/docker/docker/api/types"
11 10
 	"github.com/docker/docker/api/types/container"
12 11
 	"github.com/docker/docker/api/types/events"
13 12
 	"github.com/docker/docker/api/types/filters"
... ...
@@ -111,7 +110,7 @@ func TestDaemonRestartKillContainers(t *testing.T) {
111 111
 						err = apiClient.ContainerStart(ctx, resp.ID, container.StartOptions{})
112 112
 						assert.NilError(t, err)
113 113
 						if tc.xHealthCheck {
114
-							poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, resp.ID, types.Healthy), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
114
+							poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, resp.ID, container.Healthy), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
115 115
 							testContainer.ExecT(ctx, t, apiClient, resp.ID, []string{"touch", "/tmp/unhealthy"}).AssertSuccess(t)
116 116
 						}
117 117
 					}
... ...
@@ -132,11 +131,11 @@ func TestDaemonRestartKillContainers(t *testing.T) {
132 132
 						// to become healthy, which gives us the entire StartPeriod (60s) to assert that
133 133
 						// the container's health state is Starting before we have to worry about racing
134 134
 						// the health monitor.
135
-						assert.Equal(t, testContainer.Inspect(ctx, t, apiClient, resp.ID).State.Health.Status, types.Starting)
135
+						assert.Equal(t, testContainer.Inspect(ctx, t, apiClient, resp.ID).State.Health.Status, container.Starting)
136 136
 						poll.WaitOn(t, pollForNewHealthCheck(ctx, apiClient, startTime, resp.ID), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
137 137
 
138 138
 						testContainer.ExecT(ctx, t, apiClient, resp.ID, []string{"rm", "/tmp/unhealthy"}).AssertSuccess(t)
139
-						poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, resp.ID, types.Healthy), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
139
+						poll.WaitOn(t, pollForHealthStatus(ctx, apiClient, resp.ID, container.Healthy), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
140 140
 					}
141 141
 					// TODO(cpuguy83): test pause states... this seems to be rather undefined currently
142 142
 				})