mirror of
https://github.com/docker/compose.git
synced 2026-10-12 02:13:08 +00:00
fix: start lists service containers through getContainers
The start flow hand-rolled its ContainerList without the ConfigHashLabel
presence filter every other listing applies (getDefaultFilters) — the
actual source of pre_start hook runners leaking into startService and
waitDependencies, patched downstream by isNotHookContainer. Routing
through getContainers fixes the leak at the source, like
getContainersByService already does; the isNotHookContainer guards stay
as defense-in-depth.
The dry-run client keyed its ContainerList interception on the calling
function: caller "start" answered from the faked-creations cache, while
caller "getContainers" fell back to the real daemon once the cache was
seeded — so routing start through getContainers made dry-run-created
containers invisible to the start phase ("service has no container to
start", TestUpDryRunMissingImage). The getContainers case now keeps
answering from the seeded cache, where faked creations land.
Signed-off-by: Nicolas De Loof <[email protected]>
This commit is contained in:
committed by
Nicolas De loof
parent
329b3ad548
commit
730a785bd4
@@ -23,7 +23,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
@@ -53,14 +52,14 @@ func (s *composeService) start(ctx context.Context, projectName string, options
|
||||
// and the dependency waits read them
|
||||
project = project.WithoutUnresolvedOptionalDependencies()
|
||||
|
||||
res, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
Filters: projectFilter(project.Name).Add("label", oneOffFilter(false)),
|
||||
All: true,
|
||||
})
|
||||
// getContainers filters on ConfigHashLabel presence (getDefaultFilters),
|
||||
// which every service container carries and hook runners deliberately do
|
||||
// not: pre_start runners never leak into the start flow at the source
|
||||
// (isNotHookContainer downstream stays as defense-in-depth).
|
||||
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containers := Containers(res.Items)
|
||||
|
||||
err = InDependencyOrder(ctx, project, func(c context.Context, name string) error {
|
||||
service, err := project.GetService(name)
|
||||
|
||||
+22
-15
@@ -47,10 +47,13 @@ var _ client.APIClient = &DryRunClient{}
|
||||
|
||||
// DryRunClient implements APIClient by delegating to implementation functions. This allows lazy init and per-method overrides
|
||||
type DryRunClient struct {
|
||||
apiClient client.APIClient
|
||||
containers []containerType.Summary
|
||||
execs sync.Map
|
||||
configFile *configfile.ConfigFile
|
||||
apiClient client.APIClient
|
||||
// containers caches the project's containers: seeded once from the real
|
||||
// daemon (containersSeeded), then extended with every faked creation.
|
||||
containers []containerType.Summary
|
||||
containersSeeded bool
|
||||
execs sync.Map
|
||||
configFile *configfile.ConfigFile
|
||||
}
|
||||
|
||||
type execDetails struct {
|
||||
@@ -182,20 +185,24 @@ func (d *DryRunClient) ContainerKill(ctx context.Context, container string, opti
|
||||
func (d *DryRunClient) ContainerList(ctx context.Context, options client.ContainerListOptions) (client.ContainerListResult, error) {
|
||||
caller := getCallingFunction()
|
||||
switch caller {
|
||||
case "start":
|
||||
case "getContainers":
|
||||
// Seed the cache from the real daemon on first use, then keep
|
||||
// answering from it: containers faked by ContainerCreate must stay
|
||||
// visible to the start flow, which lists through getContainers.
|
||||
// Seeding is tracked independently of the cache's length — a faked
|
||||
// creation may land before the first listing, and must not mask the
|
||||
// daemon's real containers.
|
||||
if !d.containersSeeded {
|
||||
res, err := d.apiClient.ContainerList(ctx, options)
|
||||
if err != nil {
|
||||
return client.ContainerListResult{}, err
|
||||
}
|
||||
d.containersSeeded = true
|
||||
d.containers = append(d.containers, res.Items...)
|
||||
}
|
||||
return client.ContainerListResult{
|
||||
Items: d.containers,
|
||||
}, nil
|
||||
case "getContainers":
|
||||
if len(d.containers) == 0 {
|
||||
res, err := d.apiClient.ContainerList(ctx, options)
|
||||
if err == nil {
|
||||
d.containers = res.Items
|
||||
}
|
||||
return client.ContainerListResult{
|
||||
Items: d.containers,
|
||||
}, err
|
||||
}
|
||||
case "listPreStartRunners":
|
||||
// Hook runners created under dry-run exist only in the cache: answer
|
||||
// from it, honoring the caller's label filters (project, service,
|
||||
|
||||
Reference in New Issue
Block a user