Merge pull request #1554 from docker/poll_completed

fix race condition with init containers
This commit is contained in:
Nicolas De loof 2021-04-22 09:05:33 +02:00 committed by GitHub
commit bce61295ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 45 additions and 57 deletions

2
go.mod
View File

@ -17,7 +17,7 @@ require (
github.com/awslabs/goformation/v4 v4.15.6 github.com/awslabs/goformation/v4 v4.15.6
github.com/buger/goterm v1.0.0 github.com/buger/goterm v1.0.0
github.com/cnabio/cnab-to-oci v0.3.1-beta1 github.com/cnabio/cnab-to-oci v0.3.1-beta1
github.com/compose-spec/compose-go v0.0.0-20210412113016-e35895260882 github.com/compose-spec/compose-go v0.0.0-20210421142148-66b18e67d2c0
github.com/containerd/console v1.0.1 github.com/containerd/console v1.0.1
github.com/containerd/containerd v1.4.3 github.com/containerd/containerd v1.4.3
github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a // indirect github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a // indirect

4
go.sum
View File

@ -308,8 +308,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/compose-spec/compose-go v0.0.0-20210412113016-e35895260882 h1:vLKOiHY9QPS1eiBvGfsEI7wR23B1bGNvwOF5aVl25Fs= github.com/compose-spec/compose-go v0.0.0-20210421142148-66b18e67d2c0 h1:aoUSQGFmWi8dn1OfT4H5Mx48u5wHt+n07nLFw5j1JXs=
github.com/compose-spec/compose-go v0.0.0-20210412113016-e35895260882/go.mod h1:6eIT9U2OgdHmkRD6szmqatCrWWEEUSwl/j2iJYH4jLo= github.com/compose-spec/compose-go v0.0.0-20210421142148-66b18e67d2c0/go.mod h1:6eIT9U2OgdHmkRD6szmqatCrWWEEUSwl/j2iJYH4jLo=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340 h1:9atoWyI9RtXFwf7UDbme/6M8Ud0rFrx+Q3ZWgSnsxtw= github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340 h1:9atoWyI9RtXFwf7UDbme/6M8Ud0rFrx+Q3ZWgSnsxtw=

View File

@ -19,9 +19,7 @@ package compose
import ( import (
"context" "context"
"fmt" "fmt"
"net/url"
"os" "os"
"path"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/types"
@ -50,7 +48,7 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
if service.Build != nil { if service.Build != nil {
imageName := getImageName(service, project.Name) imageName := getImageName(service, project.Name)
imagesToBuild = append(imagesToBuild, imageName) imagesToBuild = append(imagesToBuild, imageName)
buildOptions, err := s.toBuildOptions(service, project.WorkingDir, imageName) buildOptions, err := s.toBuildOptions(service, imageName)
if err != nil { if err != nil {
return err return err
} }
@ -132,7 +130,7 @@ func (s *composeService) getBuildOptions(project *types.Project, images map[stri
continue continue
} }
imagesToBuild = append(imagesToBuild, imageName) imagesToBuild = append(imagesToBuild, imageName)
opt, err := s.toBuildOptions(service, project.WorkingDir, imageName) opt, err := s.toBuildOptions(service, imageName)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -257,13 +255,10 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opts
return imagesBuilt, err return imagesBuilt, err
} }
func (s *composeService) toBuildOptions(service types.ServiceConfig, contextPath string, imageTag string) (build.Options, error) { func (s *composeService) toBuildOptions(service types.ServiceConfig, imageTag string) (build.Options, error) {
var tags []string var tags []string
tags = append(tags, imageTag) tags = append(tags, imageTag)
if service.Build.Dockerfile == "" {
service.Build.Dockerfile = "Dockerfile"
}
var buildArgs map[string]string var buildArgs map[string]string
var plats []specs.Platform var plats []specs.Platform
@ -275,22 +270,11 @@ func (s *composeService) toBuildOptions(service types.ServiceConfig, contextPath
plats = append(plats, p) plats = append(plats, p)
} }
var input build.Inputs return build.Options{
_, err := url.ParseRequestURI(service.Build.Context) Inputs: build.Inputs{
if err == nil {
input = build.Inputs{
ContextPath: service.Build.Context, ContextPath: service.Build.Context,
DockerfilePath: service.Build.Dockerfile, DockerfilePath: service.Build.Dockerfile,
} },
} else {
input = build.Inputs{
ContextPath: path.Join(contextPath, service.Build.Context),
DockerfilePath: path.Join(contextPath, service.Build.Context, service.Build.Dockerfile),
}
}
return build.Options{
Inputs: input,
BuildArgs: flatten(mergeArgs(service.Build.Args, buildArgs)), BuildArgs: flatten(mergeArgs(service.Build.Args, buildArgs)),
Tags: tags, Tags: tags,
Target: service.Build.Target, Target: service.Build.Target,

View File

@ -25,10 +25,10 @@ import (
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/types"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
moby "github.com/docker/docker/api/types" moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
specs "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/compose"
@ -147,13 +147,13 @@ func getContainerProgressName(container moby.Container) string {
func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, service types.ServiceConfig) error { func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
eg, _ := errgroup.WithContext(ctx) eg, _ := errgroup.WithContext(ctx)
for dep, config := range service.DependsOn { for dep, config := range service.DependsOn {
switch config.Condition { eg.Go(func() error {
case "service_healthy": ticker := time.NewTicker(500 * time.Millisecond)
eg.Go(func() error { defer ticker.Stop()
ticker := time.NewTicker(500 * time.Millisecond) for {
defer ticker.Stop() <-ticker.C
for { switch config.Condition {
<-ticker.C case types.ServiceConditionHealthy:
healthy, err := s.isServiceHealthy(ctx, project, dep) healthy, err := s.isServiceHealthy(ctx, project, dep)
if err != nil { if err != nil {
return err return err
@ -161,17 +161,26 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
if healthy { if healthy {
return nil return nil
} }
case types.ServiceConditionCompletedSuccessfully:
exited, code, err := s.isServiceCompleted(ctx, project, dep)
if err != nil {
return err
}
if exited {
if code != 0 {
return fmt.Errorf("service %q didn't completed successfully: exit %d", dep, code)
}
return nil
}
case types.ServiceConditionStarted:
// already managed by InDependencyOrder
return nil
default:
logrus.Warnf("unsupported depends_on condition: %s", config.Condition)
return nil
} }
})
case "service_completed_successfully":
exit, err := s.waitCompleted(ctx, project, dep)
if err != nil {
return err
} }
if exit != 0 { })
return fmt.Errorf("service %q didn't completed successfully: exit %d", dep, exit)
}
}
} }
return eg.Wait() return eg.Wait()
} }
@ -360,26 +369,21 @@ func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Pr
return true, nil return true, nil
} }
func (s *composeService) waitCompleted(ctx context.Context, project *types.Project, dep string) (int64, error) { func (s *composeService) isServiceCompleted(ctx context.Context, project *types.Project, dep string) (bool, int, error) {
containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, dep)
Filters: filters.NewArgs(
projectFilter(project.Name),
serviceFilter(dep),
),
})
if err != nil { if err != nil {
return 0, err return false, 0, err
} }
for _, c := range containers { for _, c := range containers {
wait, errors := s.apiClient.ContainerWait(ctx, c.ID, container.WaitConditionNextExit) container, err := s.apiClient.ContainerInspect(ctx, c.ID)
select { if err != nil {
case w := <-wait: return false, 0, err
return w.StatusCode, nil }
case err := <-errors: if container.State != nil && container.State.Status == "exited" {
return 0, err return true, container.State.ExitCode, nil
} }
} }
return 0, nil return false, 0, nil
} }
func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig) error { func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig) error {