pkg/compose: use state consts from moby API

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2025-09-06 20:19:46 +02:00 committed by Nicolas De loof
parent 02ffe2ac6c
commit f217207876
4 changed files with 58 additions and 64 deletions

View File

@ -20,23 +20,17 @@ import (
"io" "io"
moby "github.com/docker/docker/api/types" moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
) )
const ( const (
// ContainerCreated created status ContainerCreated = container.StateCreated // StateCreated indicates the container is created, but not (yet) started.
ContainerCreated = "created" ContainerRunning = container.StateRunning // StateRunning indicates that the container is running.
// ContainerRestarting restarting status ContainerPaused = container.StatePaused // StatePaused indicates that the container's current state is paused.
ContainerRestarting = "restarting" ContainerRestarting = container.StateRestarting // StateRestarting indicates that the container is currently restarting.
// ContainerRunning running status ContainerRemoving = container.StateRemoving // StateRemoving indicates that the container is being removed.
ContainerRunning = "running" ContainerExited = container.StateExited // StateExited indicates that the container exited.
// ContainerRemoving removing status ContainerDead = container.StateDead // StateDead indicates that the container failed to be deleted. Containers in this state are attempted to be cleaned up when the daemon restarts.
ContainerRemoving = "removing"
// ContainerPaused paused status
ContainerPaused = "paused"
// ContainerExited exited status
ContainerExited = "exited"
// ContainerDead dead status
ContainerDead = "dead"
) )
var _ io.ReadCloser = ContainerStdout{} var _ io.ReadCloser = ContainerStdout{}

View File

@ -135,7 +135,7 @@ func isOrphaned(project *types.Project) containerPredicate {
// One-off container // One-off container
v, ok := c.Labels[api.OneoffLabel] v, ok := c.Labels[api.OneoffLabel]
if ok && v == "True" { if ok && v == "True" {
return c.State == ContainerExited || c.State == ContainerDead return c.State == container.StateExited || c.State == container.StateDead
} }
// Service that is not defined in the compose model // Service that is not defined in the compose model
service := c.Labels[api.ServiceLabel] service := c.Labels[api.ServiceLabel]

View File

@ -30,7 +30,7 @@ import (
"github.com/compose-spec/compose-go/v2/types" "github.com/compose-spec/compose-go/v2/types"
"github.com/containerd/platforms" "github.com/containerd/platforms"
containerType "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
mmount "github.com/docker/docker/api/types/mount" mmount "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
specs "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/image-spec/specs-go/v1"
@ -152,19 +152,19 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
}) })
slices.Reverse(containers) slices.Reverse(containers)
for i, container := range containers { for i, ctr := range containers {
if i >= expected { if i >= expected {
// Scale Down // Scale Down
// As we sorted containers, obsolete ones and/or highest number will be removed // As we sorted containers, obsolete ones and/or highest number will be removed
container := container ctr := ctr
traceOpts := append(tracing.ServiceOptions(service), tracing.ContainerOptions(container)...) traceOpts := append(tracing.ServiceOptions(service), tracing.ContainerOptions(ctr)...)
eg.Go(tracing.SpanWrapFuncForErrGroup(ctx, "service/scale/down", traceOpts, func(ctx context.Context) error { eg.Go(tracing.SpanWrapFuncForErrGroup(ctx, "service/scale/down", traceOpts, func(ctx context.Context) error {
return c.service.stopAndRemoveContainer(ctx, container, &service, timeout, false) return c.service.stopAndRemoveContainer(ctx, ctr, &service, timeout, false)
})) }))
continue continue
} }
mustRecreate, err := c.mustRecreate(service, container, recreate) mustRecreate, err := c.mustRecreate(service, ctr, recreate)
if err != nil { if err != nil {
return err return err
} }
@ -174,9 +174,9 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
return err return err
} }
i, container := i, container i, ctr := i, ctr
eg.Go(tracing.SpanWrapFuncForErrGroup(ctx, "container/recreate", tracing.ContainerOptions(container), func(ctx context.Context) error { eg.Go(tracing.SpanWrapFuncForErrGroup(ctx, "container/recreate", tracing.ContainerOptions(ctr), func(ctx context.Context) error {
recreated, err := c.service.recreateContainer(ctx, project, service, container, inherit, timeout) recreated, err := c.service.recreateContainer(ctx, project, service, ctr, inherit, timeout)
updated[i] = recreated updated[i] = recreated
return err return err
})) }))
@ -185,20 +185,20 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
// Enforce non-diverged containers are running // Enforce non-diverged containers are running
w := progress.ContextWriter(ctx) w := progress.ContextWriter(ctx)
name := getContainerProgressName(container) name := getContainerProgressName(ctr)
switch container.State { switch ctr.State {
case ContainerRunning: case container.StateRunning:
w.Event(progress.RunningEvent(name)) w.Event(progress.RunningEvent(name))
case ContainerCreated: case container.StateCreated:
case ContainerRestarting: case container.StateRestarting:
case ContainerExited: case container.StateExited:
default: default:
container := container ctr := ctr
eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "service/start", tracing.ContainerOptions(container), func(ctx context.Context) error { eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "service/start", tracing.ContainerOptions(ctr), func(ctx context.Context) error {
return c.service.startContainer(ctx, container) return c.service.startContainer(ctx, ctr)
})) }))
} }
updated[i] = container updated[i] = ctr
} }
next := nextContainerNumber(containers) next := nextContainerNumber(containers)
@ -214,8 +214,8 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
UseNetworkAliases: true, UseNetworkAliases: true,
Labels: mergeLabels(service.Labels, service.CustomLabels), Labels: mergeLabels(service.Labels, service.CustomLabels),
} }
container, err := c.service.createContainer(ctx, project, service, name, number, opts) ctr, err := c.service.createContainer(ctx, project, service, name, number, opts)
updated[actual+i] = container updated[actual+i] = ctr
return err return err
})) }))
continue continue
@ -245,7 +245,7 @@ func (c *convergence) stopDependentContainers(ctx context.Context, project *type
for _, name := range dependents { for _, name := range dependents {
dependentStates := c.getObservedState(name) dependentStates := c.getObservedState(name)
for i, dependent := range dependentStates { for i, dependent := range dependentStates {
dependent.State = ContainerExited dependent.State = container.StateExited
dependentStates[i] = dependent dependentStates[i] = dependent
} }
c.setObservedState(name, dependentStates) c.setObservedState(name, dependentStates)
@ -328,7 +328,7 @@ func (c *convergence) resolveSharedNamespaces(service *types.ServiceConfig) erro
return nil return nil
} }
func (c *convergence) mustRecreate(expected types.ServiceConfig, actual containerType.Summary, policy string) (bool, error) { func (c *convergence) mustRecreate(expected types.ServiceConfig, actual container.Summary, policy string) (bool, error) {
if policy == api.RecreateNever { if policy == api.RecreateNever {
return false, nil return false, nil
} }
@ -360,7 +360,7 @@ func (c *convergence) mustRecreate(expected types.ServiceConfig, actual containe
return false, nil return false, nil
} }
func checkExpectedNetworks(expected types.ServiceConfig, actual containerType.Summary, networks map[string]string) bool { func checkExpectedNetworks(expected types.ServiceConfig, actual container.Summary, networks map[string]string) bool {
// check the networks container is connected to are the expected ones // check the networks container is connected to are the expected ones
for net := range expected.Networks { for net := range expected.Networks {
id := networks[net] id := networks[net]
@ -383,7 +383,7 @@ func checkExpectedNetworks(expected types.ServiceConfig, actual containerType.Su
return false return false
} }
func checkExpectedVolumes(expected types.ServiceConfig, actual containerType.Summary, volumes map[string]string) bool { func checkExpectedVolumes(expected types.ServiceConfig, actual container.Summary, volumes map[string]string) bool {
// check container's volume mounts and search for the expected ones // check container's volume mounts and search for the expected ones
for _, vol := range expected.Volumes { for _, vol := range expected.Volumes {
if vol.Type != string(mmount.TypeVolume) { if vol.Type != string(mmount.TypeVolume) {
@ -423,7 +423,7 @@ func getDefaultContainerName(projectName, serviceName, index string) string {
return strings.Join([]string{projectName, serviceName, index}, api.Separator) return strings.Join([]string{projectName, serviceName, index}, api.Separator)
} }
func getContainerProgressName(ctr containerType.Summary) string { func getContainerProgressName(ctr container.Summary) string {
return "Container " + getCanonicalContainerName(ctr) return "Container " + getCanonicalContainerName(ctr)
} }
@ -571,7 +571,7 @@ func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceD
return true, nil return true, nil
} }
func nextContainerNumber(containers []containerType.Summary) int { func nextContainerNumber(containers []container.Summary) int {
maxNumber := 0 maxNumber := 0
for _, c := range containers { for _, c := range containers {
s, ok := c.Labels[api.ContainerNumberLabel] s, ok := c.Labels[api.ContainerNumberLabel]
@ -592,7 +592,7 @@ func nextContainerNumber(containers []containerType.Summary) int {
func (s *composeService) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, func (s *composeService) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
name string, number int, opts createOptions, name string, number int, opts createOptions,
) (ctr containerType.Summary, err error) { ) (ctr container.Summary, err error) {
w := progress.ContextWriter(ctx) w := progress.ContextWriter(ctx)
eventName := "Container " + name eventName := "Container " + name
w.Event(progress.CreatingEvent(eventName)) w.Event(progress.CreatingEvent(eventName))
@ -612,8 +612,8 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
} }
func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
replaced containerType.Summary, inherit bool, timeout *time.Duration, replaced container.Summary, inherit bool, timeout *time.Duration,
) (created containerType.Summary, err error) { ) (created container.Summary, err error) {
w := progress.ContextWriter(ctx) w := progress.ContextWriter(ctx)
eventName := getContainerProgressName(replaced) eventName := getContainerProgressName(replaced)
w.Event(progress.NewEvent(eventName, progress.Working, "Recreate")) w.Event(progress.NewEvent(eventName, progress.Working, "Recreate"))
@ -632,7 +632,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
return created, err return created, err
} }
var inherited *containerType.Summary var inherited *container.Summary
if inherit { if inherit {
inherited = &replaced inherited = &replaced
} }
@ -655,12 +655,12 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
} }
timeoutInSecond := utils.DurationSecondToInt(timeout) timeoutInSecond := utils.DurationSecondToInt(timeout)
err = s.apiClient().ContainerStop(ctx, replaced.ID, containerType.StopOptions{Timeout: timeoutInSecond}) err = s.apiClient().ContainerStop(ctx, replaced.ID, container.StopOptions{Timeout: timeoutInSecond})
if err != nil { if err != nil {
return created, err return created, err
} }
err = s.apiClient().ContainerRemove(ctx, replaced.ID, containerType.RemoveOptions{}) err = s.apiClient().ContainerRemove(ctx, replaced.ID, container.RemoveOptions{})
if err != nil { if err != nil {
return created, err return created, err
} }
@ -677,12 +677,12 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
// force sequential calls to ContainerStart to prevent race condition in engine assigning ports from ranges // force sequential calls to ContainerStart to prevent race condition in engine assigning ports from ranges
var startMx sync.Mutex var startMx sync.Mutex
func (s *composeService) startContainer(ctx context.Context, ctr containerType.Summary) error { func (s *composeService) startContainer(ctx context.Context, ctr container.Summary) error {
w := progress.ContextWriter(ctx) w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(getContainerProgressName(ctr), progress.Working, "Restart")) w.Event(progress.NewEvent(getContainerProgressName(ctr), progress.Working, "Restart"))
startMx.Lock() startMx.Lock()
defer startMx.Unlock() defer startMx.Unlock()
err := s.apiClient().ContainerStart(ctx, ctr.ID, containerType.StartOptions{}) err := s.apiClient().ContainerStart(ctx, ctr.ID, container.StartOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -695,11 +695,11 @@ func (s *composeService) createMobyContainer(ctx context.Context,
service types.ServiceConfig, service types.ServiceConfig,
name string, name string,
number int, number int,
inherit *containerType.Summary, inherit *container.Summary,
opts createOptions, opts createOptions,
w progress.Writer, w progress.Writer,
) (containerType.Summary, error) { ) (container.Summary, error) {
var created containerType.Summary var created container.Summary
cfgs, err := s.getCreateConfigs(ctx, project, service, number, inherit, opts) cfgs, err := s.getCreateConfigs(ctx, project, service, number, inherit, opts)
if err != nil { if err != nil {
return created, err return created, err
@ -733,11 +733,11 @@ func (s *composeService) createMobyContainer(ctx context.Context,
if err != nil { if err != nil {
return created, err return created, err
} }
created = containerType.Summary{ created = container.Summary{
ID: inspectedContainer.ID, ID: inspectedContainer.ID,
Labels: inspectedContainer.Config.Labels, Labels: inspectedContainer.Config.Labels,
Names: []string{inspectedContainer.Name}, Names: []string{inspectedContainer.Name},
NetworkSettings: &containerType.NetworkSettingsSummary{ NetworkSettings: &container.NetworkSettingsSummary{
Networks: inspectedContainer.NetworkSettings.Networks, Networks: inspectedContainer.NetworkSettings.Networks,
}, },
} }
@ -834,24 +834,24 @@ func (s *composeService) isServiceHealthy(ctx context.Context, containers Contai
} }
name := ctr.Name[1:] name := ctr.Name[1:]
if ctr.State.Status == containerType.StateExited { if ctr.State.Status == container.StateExited {
return false, fmt.Errorf("container %s exited (%d)", name, ctr.State.ExitCode) return false, fmt.Errorf("container %s exited (%d)", name, ctr.State.ExitCode)
} }
if ctr.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 ctr.State != nil && ctr.State.Status == containerType.StateRunning, nil return ctr.State != nil && ctr.State.Status == container.StateRunning, nil
} }
if ctr.State == nil || ctr.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 ctr.State.Health.Status { switch ctr.State.Health.Status {
case containerType.Healthy: case container.Healthy:
// Continue by checking the next container. // Continue by checking the next container.
case containerType.Unhealthy: case container.Unhealthy:
return false, fmt.Errorf("container %s is unhealthy", name) return false, fmt.Errorf("container %s is unhealthy", name)
case containerType.Starting: case container.Starting:
return false, nil return false, nil
default: default:
return false, fmt.Errorf("container %s had unexpected health status %q", name, ctr.State.Health.Status) return false, fmt.Errorf("container %s had unexpected health status %q", name, ctr.State.Health.Status)
@ -866,7 +866,7 @@ func (s *composeService) isServiceCompleted(ctx context.Context, containers Cont
if err != nil { if err != nil {
return false, 0, err return false, 0, err
} }
if ctr.State != nil && ctr.State.Status == containerType.StateExited { if ctr.State != nil && ctr.State.Status == container.StateExited {
return true, ctr.State.ExitCode, nil return true, ctr.State.ExitCode, nil
} }
} }
@ -896,7 +896,7 @@ func (s *composeService) startService(ctx context.Context,
w := progress.ContextWriter(ctx) w := progress.ContextWriter(ctx)
for _, ctr := range containers.filter(isService(service.Name)) { for _, ctr := range containers.filter(isService(service.Name)) {
if ctr.State == ContainerRunning { if ctr.State == container.StateRunning {
continue continue
} }
@ -912,7 +912,7 @@ func (s *composeService) startService(ctx context.Context,
eventName := getContainerProgressName(ctr) eventName := getContainerProgressName(ctr)
w.Event(progress.StartingEvent(eventName)) w.Event(progress.StartingEvent(eventName))
err = s.apiClient().ContainerStart(ctx, ctr.ID, containerType.StartOptions{}) err = s.apiClient().ContainerStart(ctx, ctr.ID, container.StartOptions{})
if err != nil { if err != nil {
return err return err
} }

View File

@ -111,7 +111,7 @@ func testContainer(service string, id string, oneOff bool) container.Summary {
ID: id, ID: id,
Names: []string{name}, Names: []string{name},
Labels: containerLabels(service, oneOff), Labels: containerLabels(service, oneOff),
State: ContainerExited, State: container.StateExited,
} }
} }