workaround race condition in ContainerList

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2023-03-28 09:43:16 +02:00
parent 67c9ecb4f4
commit c5317496ac
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
3 changed files with 44 additions and 19 deletions

View File

@ -64,16 +64,6 @@ func runRemove(ctx context.Context, backend api.Service, opts removeOptions, ser
return err return err
} }
if opts.stop {
err := backend.Stop(ctx, name, api.StopOptions{
Services: services,
Project: project,
})
if err != nil {
return err
}
}
return backend.Remove(ctx, name, api.RemoveOptions{ return backend.Remove(ctx, name, api.RemoveOptions{
Services: services, Services: services,
Force: opts.force, Force: opts.force,

View File

@ -31,6 +31,17 @@ import (
func (s *composeService) Remove(ctx context.Context, projectName string, options api.RemoveOptions) error { func (s *composeService) Remove(ctx context.Context, projectName string, options api.RemoveOptions) error {
projectName = strings.ToLower(projectName) projectName = strings.ToLower(projectName)
if options.Stop {
err := s.Stop(ctx, projectName, api.StopOptions{
Services: options.Services,
Project: options.Project,
})
if err != nil {
return err
}
}
containers, err := s.getContainers(ctx, projectName, oneOffExclude, true, options.Services...) containers, err := s.getContainers(ctx, projectName, oneOffExclude, true, options.Services...)
if err != nil { if err != nil {
if api.IsNotFoundError(err) { if api.IsNotFoundError(err) {
@ -44,14 +55,27 @@ func (s *composeService) Remove(ctx context.Context, projectName string, options
containers = containers.filter(isService(options.Project.ServiceNames()...)) containers = containers.filter(isService(options.Project.ServiceNames()...))
} }
stoppedContainers := containers.filter(func(c moby.Container) bool { var stoppedContainers Containers
return c.State != ContainerRunning || (options.Stop && s.dryRun) for _, container := range containers {
}) // We have to inspect containers, as State reported by getContainers suffers a race condition
inspected, err := s.apiClient().ContainerInspect(ctx, container.ID)
if api.IsNotFoundError(err) {
// Already removed. Maybe configured with auto-remove
continue
}
if err != nil {
return err
}
if !inspected.State.Running || (options.Stop && s.dryRun) {
stoppedContainers = append(stoppedContainers, container)
}
}
var names []string var names []string
stoppedContainers.forEach(func(c moby.Container) { stoppedContainers.forEach(func(c moby.Container) {
names = append(names, getCanonicalContainerName(c)) names = append(names, getCanonicalContainerName(c))
}) })
fmt.Fprintln(s.stderr(), names)
if len(names) == 0 { if len(names) == 0 {
fmt.Fprintln(s.stderr(), "No stopped containers") fmt.Fprintln(s.stderr(), "No stopped containers")

View File

@ -169,23 +169,34 @@ func TestRm(t *testing.T) {
c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "up", "-d") c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "up", "-d")
}) })
t.Run("rm -sf", func(t *testing.T) { t.Run("rm --stop --force simple", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm", res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm",
"-sf", "simple") "--stop", "--force", "simple")
res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0}) res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0})
}) })
t.Run("check containers after rm -sf", func(t *testing.T) { t.Run("check containers after rm", func(t *testing.T) {
res := c.RunDockerCmd(t, "ps", "--all") res := c.RunDockerCmd(t, "ps", "--all")
assert.Assert(t, !strings.Contains(res.Combined(), projectName+"_simple"), res.Combined()) assert.Assert(t, !strings.Contains(res.Combined(), projectName+"-simple"), res.Combined())
assert.Assert(t, strings.Contains(res.Combined(), projectName+"-another"), res.Combined())
}) })
t.Run("rm -sf <none>", func(t *testing.T) { t.Run("up (again)", func(t *testing.T) {
c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "up", "-d")
})
t.Run("rm ---stop --force <none>", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm", res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm",
"-sf", "simple") "--stop", "--force")
res.Assert(t, icmd.Expected{ExitCode: 0}) res.Assert(t, icmd.Expected{ExitCode: 0})
}) })
t.Run("check containers after rm", func(t *testing.T) {
res := c.RunDockerCmd(t, "ps", "--all")
assert.Assert(t, !strings.Contains(res.Combined(), projectName+"-simple"), res.Combined())
assert.Assert(t, !strings.Contains(res.Combined(), projectName+"-another"), res.Combined())
})
t.Run("down", func(t *testing.T) { t.Run("down", func(t *testing.T) {
c.RunDockerComposeCmd(t, "-p", projectName, "down") c.RunDockerComposeCmd(t, "-p", projectName, "down")
}) })