mirror of
https://github.com/docker/compose.git
synced 2026-10-12 02:13:08 +00:00
test(e2e): batch snapshot inspect with per-container fallback, explicit suite timeout
The per-container inspect of the previous fix doubled the suite's wall-clock on the containerd CI job — one docker CLI process per container per snapshot — pushing it past go test's default 10m and killing the run mid-flight. Snapshots go back to one batch inspect, falling back to individual inspects only when the batch fails because a helper container vanished. The e2e make targets also pass an explicit -timeout 20m so a slow job fails on its own results, not on the default timeout. TestUpStopWithLogsMixed becomes deterministic: with a bounded ping the service could exit on its own before the abort reached it and the pre_stop hook never ran; it now pings forever so the abort always interrupts it. Signed-off-by: Nicolas De Loof <[email protected]>
This commit is contained in:
+49
-35
@@ -324,47 +324,27 @@ func (s *Scenario) command(action Action) icmd.Cmd {
|
||||
}
|
||||
|
||||
// snapshot captures the current state of the project's containers.
|
||||
// Containers are inspected one by one: helper containers (e.g. the ones
|
||||
// running lifecycle hooks) carry the project label but can vanish between the
|
||||
// listing and the inspect, and one gone container must not blank the whole
|
||||
// The containers are inspected in one batch; if that fails — helper
|
||||
// containers (e.g. the ones running lifecycle hooks) carry the project label
|
||||
// but can vanish between the listing and the inspect — each container is
|
||||
// retried individually so one gone container does not blank the whole
|
||||
// snapshot. Helpers are also excluded from the snapshot itself: a container
|
||||
// belongs to a service if it is numbered as a replica or marked one-off.
|
||||
func (s *Scenario) snapshot() snapshot {
|
||||
s.t.Helper()
|
||||
snap := snapshot{}
|
||||
for _, id := range s.projectContainerIDs() {
|
||||
res := icmd.RunCmd(s.cli.NewDockerCmd(s.t, "inspect", id))
|
||||
if res.ExitCode != 0 {
|
||||
continue
|
||||
}
|
||||
var containers []struct {
|
||||
ID string `json:"Id"`
|
||||
Name string `json:"Name"`
|
||||
State struct {
|
||||
Status string `json:"Status"`
|
||||
StartedAt string `json:"StartedAt"`
|
||||
} `json:"State"`
|
||||
Config struct {
|
||||
Labels map[string]string `json:"Labels"`
|
||||
} `json:"Config"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(res.Stdout()), &containers); err != nil {
|
||||
continue
|
||||
}
|
||||
for _, c := range containers {
|
||||
oneOff := c.Config.Labels["com.docker.compose.oneoff"] == "True"
|
||||
if !oneOff && c.Config.Labels["com.docker.compose.container-number"] == "" {
|
||||
continue
|
||||
ids := s.projectContainerIDs()
|
||||
if len(ids) == 0 {
|
||||
return snap
|
||||
}
|
||||
res := icmd.RunCmd(s.cli.NewDockerCmd(s.t, append([]string{"inspect"}, ids...)...))
|
||||
if res.ExitCode == 0 {
|
||||
s.collect(snap, res.Stdout())
|
||||
} else {
|
||||
for _, id := range ids {
|
||||
if res := icmd.RunCmd(s.cli.NewDockerCmd(s.t, "inspect", id)); res.ExitCode == 0 {
|
||||
s.collect(snap, res.Stdout())
|
||||
}
|
||||
service := c.Config.Labels["com.docker.compose.service"]
|
||||
snap[service] = append(snap[service], containerState{
|
||||
ID: c.ID,
|
||||
Name: strings.TrimPrefix(c.Name, "/"),
|
||||
State: c.State.Status,
|
||||
StartedAt: c.State.StartedAt,
|
||||
OneOff: oneOff,
|
||||
Labels: c.Config.Labels,
|
||||
})
|
||||
}
|
||||
}
|
||||
for service := range snap {
|
||||
@@ -373,6 +353,40 @@ func (s *Scenario) snapshot() snapshot {
|
||||
return snap
|
||||
}
|
||||
|
||||
// collect parses a docker inspect output into the snapshot, skipping helper
|
||||
// containers (neither numbered as a replica nor one-off).
|
||||
func (s *Scenario) collect(snap snapshot, inspectOutput string) {
|
||||
var containers []struct {
|
||||
ID string `json:"Id"`
|
||||
Name string `json:"Name"`
|
||||
State struct {
|
||||
Status string `json:"Status"`
|
||||
StartedAt string `json:"StartedAt"`
|
||||
} `json:"State"`
|
||||
Config struct {
|
||||
Labels map[string]string `json:"Labels"`
|
||||
} `json:"Config"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(inspectOutput), &containers); err != nil {
|
||||
return
|
||||
}
|
||||
for _, c := range containers {
|
||||
oneOff := c.Config.Labels["com.docker.compose.oneoff"] == "True"
|
||||
if !oneOff && c.Config.Labels["com.docker.compose.container-number"] == "" {
|
||||
continue
|
||||
}
|
||||
service := c.Config.Labels["com.docker.compose.service"]
|
||||
snap[service] = append(snap[service], containerState{
|
||||
ID: c.ID,
|
||||
Name: strings.TrimPrefix(c.Name, "/"),
|
||||
State: c.State.Status,
|
||||
StartedAt: c.State.StartedAt,
|
||||
OneOff: oneOff,
|
||||
Labels: c.Config.Labels,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scenario) projectContainerIDs() []string {
|
||||
res := icmd.RunCmd(s.cli.NewDockerCmd(s.t, "ps", "-a", "--no-trunc",
|
||||
"--filter", "label=com.docker.compose.project="+s.project, "--format", "{{.ID}}"))
|
||||
|
||||
+6
-2
@@ -262,15 +262,19 @@ services:
|
||||
}
|
||||
|
||||
func TestUpStopWithLogsMixed(t *testing.T) {
|
||||
// service2 pings forever so the abort always interrupts it: with a bounded
|
||||
// ping, on a fast machine it can exit on its own before the abort reaches
|
||||
// it, and the pre_stop hook never runs.
|
||||
s := NewScenario(t, "on abort, logs of surviving services must keep flowing while others stop, hooks included")
|
||||
s.Compose(`
|
||||
services:
|
||||
service1:
|
||||
image: alpine
|
||||
command: /bin/true
|
||||
command: sh -c "sleep 2"
|
||||
service2:
|
||||
image: alpine
|
||||
command: ping -c 2 localhost
|
||||
init: true
|
||||
command: ping localhost
|
||||
pre_stop:
|
||||
- command: echo "stop hook running..."
|
||||
`).
|
||||
|
||||
Reference in New Issue
Block a user