use enum-consts for State and Health

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2025-08-28 23:33:09 +02:00 committed by Nicolas De loof
parent 1d52012b82
commit f1efbb8322
4 changed files with 55 additions and 48 deletions

View File

@ -130,7 +130,7 @@ func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (
ID: id, ID: id,
Name: container, Name: container,
State: &containerType.State{ State: &containerType.State{
Status: "running", // needed for --wait option Status: containerType.StateRunning, // needed for --wait option
Health: &containerType.Health{ Health: &containerType.Health{
Status: containerType.Healthy, // needed for healthcheck control Status: containerType.Healthy, // needed for healthcheck control
}, },

View File

@ -828,25 +828,25 @@ func (s *composeService) getLinks(ctx context.Context, projectName string, servi
func (s *composeService) isServiceHealthy(ctx context.Context, containers Containers, fallbackRunning bool) (bool, error) { func (s *composeService) isServiceHealthy(ctx context.Context, containers Containers, fallbackRunning bool) (bool, error) {
for _, c := range containers { for _, c := range containers {
container, err := s.apiClient().ContainerInspect(ctx, c.ID) ctr, err := s.apiClient().ContainerInspect(ctx, c.ID)
if err != nil { if err != nil {
return false, err return false, err
} }
name := container.Name[1:] name := ctr.Name[1:]
if container.State.Status == "exited" { if ctr.State.Status == containerType.StateExited {
return false, fmt.Errorf("container %s exited (%d)", name, container.State.ExitCode) return false, fmt.Errorf("container %s exited (%d)", name, ctr.State.ExitCode)
} }
if container.Config.Healthcheck == nil && fallbackRunning { if ctr.Config.Healthcheck == nil && fallbackRunning {
// Container does not define a health check, but we can fall back to "running" state // Container does not define a health check, but we can fall back to "running" state
return container.State != nil && container.State.Status == "running", nil return ctr.State != nil && ctr.State.Status == containerType.StateRunning, nil
} }
if container.State == nil || container.State.Health == nil { if ctr.State == nil || ctr.State.Health == nil {
return false, fmt.Errorf("container %s has no healthcheck configured", name) return false, fmt.Errorf("container %s has no healthcheck configured", name)
} }
switch container.State.Health.Status { switch ctr.State.Health.Status {
case containerType.Healthy: case containerType.Healthy:
// Continue by checking the next container. // Continue by checking the next container.
case containerType.Unhealthy: case containerType.Unhealthy:
@ -854,7 +854,7 @@ func (s *composeService) isServiceHealthy(ctx context.Context, containers Contai
case containerType.Starting: case containerType.Starting:
return false, nil return false, nil
default: default:
return false, fmt.Errorf("container %s had unexpected health status %q", name, container.State.Health.Status) return false, fmt.Errorf("container %s had unexpected health status %q", name, ctr.State.Health.Status)
} }
} }
return true, nil return true, nil
@ -862,12 +862,12 @@ func (s *composeService) isServiceHealthy(ctx context.Context, containers Contai
func (s *composeService) isServiceCompleted(ctx context.Context, containers Containers) (bool, int, error) { func (s *composeService) isServiceCompleted(ctx context.Context, containers Containers) (bool, int, error) {
for _, c := range containers { for _, c := range containers {
container, err := s.apiClient().ContainerInspect(ctx, c.ID) ctr, err := s.apiClient().ContainerInspect(ctx, c.ID)
if err != nil { if err != nil {
return false, 0, err return false, 0, err
} }
if container.State != nil && container.State.Status == "exited" { if ctr.State != nil && ctr.State.Status == containerType.StateExited {
return true, container.State.ExitCode, nil return true, ctr.State.ExitCode, nil
} }
} }
return false, 0, nil return false, 0, nil

View File

@ -21,6 +21,7 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/docker/docker/api/types/container"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/api"
@ -42,13 +43,13 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
} }
summary := make([]api.ContainerSummary, len(containers)) summary := make([]api.ContainerSummary, len(containers))
eg, ctx := errgroup.WithContext(ctx) eg, ctx := errgroup.WithContext(ctx)
for i, container := range containers { for i, ctr := range containers {
eg.Go(func() error { eg.Go(func() error {
publishers := make([]api.PortPublisher, len(container.Ports)) publishers := make([]api.PortPublisher, len(ctr.Ports))
sort.Slice(container.Ports, func(i, j int) bool { sort.Slice(ctr.Ports, func(i, j int) bool {
return container.Ports[i].PrivatePort < container.Ports[j].PrivatePort return ctr.Ports[i].PrivatePort < ctr.Ports[j].PrivatePort
}) })
for i, p := range container.Ports { for i, p := range ctr.Ports {
publishers[i] = api.PortPublisher{ publishers[i] = api.PortPublisher{
URL: p.IP, URL: p.IP,
TargetPort: int(p.PrivatePort), TargetPort: int(p.PrivatePort),
@ -57,22 +58,22 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
} }
} }
inspect, err := s.apiClient().ContainerInspect(ctx, container.ID) inspect, err := s.apiClient().ContainerInspect(ctx, ctr.ID)
if err != nil { if err != nil {
return err return err
} }
var ( var (
health string health container.HealthStatus
exitCode int exitCode int
) )
if inspect.State != nil { if inspect.State != nil {
switch inspect.State.Status { switch inspect.State.Status {
case "running": case container.StateRunning:
if inspect.State.Health != nil { if inspect.State.Health != nil {
health = inspect.State.Health.Status health = inspect.State.Health.Status
} }
case "exited", "dead": case container.StateExited, container.StateDead:
exitCode = inspect.State.ExitCode exitCode = inspect.State.ExitCode
} }
} }
@ -81,7 +82,7 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
local int local int
mounts []string mounts []string
) )
for _, m := range container.Mounts { for _, m := range ctr.Mounts {
name := m.Name name := m.Name
if name == "" { if name == "" {
name = m.Source name = m.Source
@ -93,26 +94,26 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
} }
var networks []string var networks []string
if container.NetworkSettings != nil { if ctr.NetworkSettings != nil {
for k := range container.NetworkSettings.Networks { for k := range ctr.NetworkSettings.Networks {
networks = append(networks, k) networks = append(networks, k)
} }
} }
summary[i] = api.ContainerSummary{ summary[i] = api.ContainerSummary{
ID: container.ID, ID: ctr.ID,
Name: getCanonicalContainerName(container), Name: getCanonicalContainerName(ctr),
Names: container.Names, Names: ctr.Names,
Image: container.Image, Image: ctr.Image,
Project: container.Labels[api.ProjectLabel], Project: ctr.Labels[api.ProjectLabel],
Service: container.Labels[api.ServiceLabel], Service: ctr.Labels[api.ServiceLabel],
Command: container.Command, Command: ctr.Command,
State: container.State, State: ctr.State,
Status: container.Status, Status: ctr.Status,
Created: container.Created, Created: ctr.Created,
Labels: container.Labels, Labels: ctr.Labels,
SizeRw: container.SizeRw, SizeRw: ctr.SizeRw,
SizeRootFs: container.SizeRootFs, SizeRootFs: ctr.SizeRootFs,
Mounts: mounts, Mounts: mounts,
LocalVolumes: local, LocalVolumes: local,
Networks: networks, Networks: networks,

View File

@ -22,11 +22,11 @@ import (
"testing" "testing"
containerType "github.com/docker/docker/api/types/container" containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"go.uber.org/mock/gomock" "go.uber.org/mock/gomock"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
compose "github.com/docker/compose/v2/pkg/api" compose "github.com/docker/compose/v2/pkg/api"
"github.com/docker/docker/api/types/filters"
) )
func TestPs(t *testing.T) { func TestPs(t *testing.T) {
@ -42,10 +42,10 @@ func TestPs(t *testing.T) {
args := filters.NewArgs(projectFilter(strings.ToLower(testProject)), hasConfigHashLabel()) args := filters.NewArgs(projectFilter(strings.ToLower(testProject)), hasConfigHashLabel())
args.Add("label", "com.docker.compose.oneoff=False") args.Add("label", "com.docker.compose.oneoff=False")
listOpts := containerType.ListOptions{Filters: args, All: false} listOpts := containerType.ListOptions{Filters: args, All: false}
c1, inspect1 := containerDetails("service1", "123", "running", "healthy", 0) c1, inspect1 := containerDetails("service1", "123", containerType.StateRunning, containerType.Healthy, 0)
c2, inspect2 := containerDetails("service1", "456", "running", "", 0) c2, inspect2 := containerDetails("service1", "456", containerType.StateRunning, "", 0)
c2.Ports = []containerType.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}} c2.Ports = []containerType.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}}
c3, inspect3 := containerDetails("service2", "789", "exited", "", 130) c3, inspect3 := containerDetails("service2", "789", containerType.StateExited, "", 130)
api.EXPECT().ContainerList(ctx, listOpts).Return([]containerType.Summary{c1, c2, c3}, nil) api.EXPECT().ContainerList(ctx, listOpts).Return([]containerType.Summary{c1, c2, c3}, nil)
api.EXPECT().ContainerInspect(anyCancellableContext(), "123").Return(inspect1, nil) api.EXPECT().ContainerInspect(anyCancellableContext(), "123").Return(inspect1, nil)
api.EXPECT().ContainerInspect(anyCancellableContext(), "456").Return(inspect2, nil) api.EXPECT().ContainerInspect(anyCancellableContext(), "456").Return(inspect2, nil)
@ -56,7 +56,9 @@ func TestPs(t *testing.T) {
expected := []compose.ContainerSummary{ expected := []compose.ContainerSummary{
{ {
ID: "123", Name: "123", Names: []string{"/123"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service1", ID: "123", Name: "123", Names: []string{"/123"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
State: "running", Health: "healthy", Publishers: []compose.PortPublisher{}, State: containerType.StateRunning,
Health: containerType.Healthy,
Publishers: []compose.PortPublisher{},
Labels: map[string]string{ Labels: map[string]string{
compose.ProjectLabel: strings.ToLower(testProject), compose.ProjectLabel: strings.ToLower(testProject),
compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml", compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml",
@ -66,7 +68,8 @@ func TestPs(t *testing.T) {
}, },
{ {
ID: "456", Name: "456", Names: []string{"/456"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service1", ID: "456", Name: "456", Names: []string{"/456"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
State: "running", Health: "", State: containerType.StateRunning,
Health: "",
Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90, PublishedPort: 80}}, Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90, PublishedPort: 80}},
Labels: map[string]string{ Labels: map[string]string{
compose.ProjectLabel: strings.ToLower(testProject), compose.ProjectLabel: strings.ToLower(testProject),
@ -77,7 +80,10 @@ func TestPs(t *testing.T) {
}, },
{ {
ID: "789", Name: "789", Names: []string{"/789"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service2", ID: "789", Name: "789", Names: []string{"/789"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service2",
State: "exited", Health: "", ExitCode: 130, Publishers: []compose.PortPublisher{}, State: containerType.StateExited,
Health: "",
ExitCode: 130,
Publishers: []compose.PortPublisher{},
Labels: map[string]string{ Labels: map[string]string{
compose.ProjectLabel: strings.ToLower(testProject), compose.ProjectLabel: strings.ToLower(testProject),
compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml", compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml",
@ -90,8 +96,8 @@ func TestPs(t *testing.T) {
assert.DeepEqual(t, containers, expected) assert.DeepEqual(t, containers, expected)
} }
func containerDetails(service string, id string, status string, health string, exitCode int) (containerType.Summary, containerType.InspectResponse) { func containerDetails(service string, id string, status containerType.ContainerState, health containerType.HealthStatus, exitCode int) (containerType.Summary, containerType.InspectResponse) {
container := containerType.Summary{ ctr := containerType.Summary{
ID: id, ID: id,
Names: []string{"/" + id}, Names: []string{"/" + id},
Image: "foo", Image: "foo",
@ -107,5 +113,5 @@ func containerDetails(service string, id string, status string, health string, e
}, },
}, },
} }
return container, inspect return ctr, inspect
} }