project.Services is a map

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2023-11-27 10:14:31 +01:00 committed by Nicolas De loof
parent cda04f288e
commit 138facea62
25 changed files with 120 additions and 124 deletions

View File

@ -23,7 +23,6 @@ import (
"strings"
"github.com/compose-spec/compose-go/v2/cli"
"github.com/compose-spec/compose-go/v2/loader"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
cliopts "github.com/docker/cli/opts"

View File

@ -26,20 +26,20 @@ import (
func TestFilterServices(t *testing.T) {
p := &types.Project{
Services: types.Services{
{
"foo": {
Name: "foo",
Links: []string{"bar"},
},
{
"bar": {
Name: "bar",
DependsOn: map[string]types.ServiceDependency{
"zot": {},
},
},
{
"zot": {
Name: "zot",
},
{
"qix": {
Name: "qix",
},
},

View File

@ -86,7 +86,7 @@ func sampleProject() *types.Project {
return &types.Project{
Name: "test",
Services: types.Services{
{
"svc": {
Name: "svc",
Build: &types.BuildConfig{
Context: ".",

View File

@ -25,10 +25,7 @@ import (
func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
defaultPlatform := project.Environment["DOCKER_DEFAULT_PLATFORM"]
for i := range project.Services {
// mutable reference so platform fields can be updated
service := &project.Services[i]
for _, service := range project.Services {
if service.Build == nil {
continue
}

View File

@ -27,7 +27,7 @@ func TestApplyPlatforms_InferFromRuntime(t *testing.T) {
makeProject := func() *types.Project {
return &types.Project{
Services: types.Services{
{
"test": {
Name: "test",
Image: "foo",
Build: &types.BuildConfig{
@ -47,14 +47,14 @@ func TestApplyPlatforms_InferFromRuntime(t *testing.T) {
t.Run("SinglePlatform", func(t *testing.T) {
project := makeProject()
require.NoError(t, applyPlatforms(project, true))
require.EqualValues(t, []string{"alice/32"}, project.Services[0].Build.Platforms)
require.EqualValues(t, []string{"alice/32"}, project.Services["test"].Build.Platforms)
})
t.Run("MultiPlatform", func(t *testing.T) {
project := makeProject()
require.NoError(t, applyPlatforms(project, false))
require.EqualValues(t, []string{"linux/amd64", "linux/arm64", "alice/32"},
project.Services[0].Build.Platforms)
project.Services["test"].Build.Platforms)
})
}
@ -65,7 +65,7 @@ func TestApplyPlatforms_DockerDefaultPlatform(t *testing.T) {
"DOCKER_DEFAULT_PLATFORM": "linux/amd64",
},
Services: types.Services{
{
"test": {
Name: "test",
Image: "foo",
Build: &types.BuildConfig{
@ -83,14 +83,14 @@ func TestApplyPlatforms_DockerDefaultPlatform(t *testing.T) {
t.Run("SinglePlatform", func(t *testing.T) {
project := makeProject()
require.NoError(t, applyPlatforms(project, true))
require.EqualValues(t, []string{"linux/amd64"}, project.Services[0].Build.Platforms)
require.EqualValues(t, []string{"linux/amd64"}, project.Services["test"].Build.Platforms)
})
t.Run("MultiPlatform", func(t *testing.T) {
project := makeProject()
require.NoError(t, applyPlatforms(project, false))
require.EqualValues(t, []string{"linux/amd64", "linux/arm64"},
project.Services[0].Build.Platforms)
project.Services["test"].Build.Platforms)
})
}
@ -101,7 +101,7 @@ func TestApplyPlatforms_UnsupportedPlatform(t *testing.T) {
"DOCKER_DEFAULT_PLATFORM": "commodore/64",
},
Services: types.Services{
{
"foo": {
Name: "test",
Image: "foo",
Build: &types.BuildConfig{

View File

@ -26,21 +26,21 @@ import (
func TestApplyPullOptions(t *testing.T) {
project := &types.Project{
Services: types.Services{
{
"must-build": {
Name: "must-build",
// No image, local build only
Build: &types.BuildConfig{
Context: ".",
},
},
{
"has-build": {
Name: "has-build",
Image: "registry.example.com/myservice",
Build: &types.BuildConfig{
Context: ".",
},
},
{
"must-pull": {
Name: "must-pull",
Image: "registry.example.com/another-service",
},
@ -51,7 +51,7 @@ func TestApplyPullOptions(t *testing.T) {
}.apply(project, nil)
assert.NilError(t, err)
assert.Equal(t, project.Services[0].PullPolicy, "") // still default
assert.Equal(t, project.Services[1].PullPolicy, types.PullPolicyMissing)
assert.Equal(t, project.Services[2].PullPolicy, types.PullPolicyMissing)
assert.Equal(t, project.Services["must-build"].PullPolicy, "") // still default
assert.Equal(t, project.Services["has-build"].PullPolicy, types.PullPolicyMissing)
assert.Equal(t, project.Services["must-pull"].PullPolicy, types.PullPolicyMissing)
}

View File

@ -300,16 +300,16 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
func startDependencies(ctx context.Context, backend api.Service, project types.Project, buildOpts *api.BuildOptions, requestedServiceName string, ignoreOrphans bool) error {
dependencies := types.Services{}
var requestedService types.ServiceConfig
for _, service := range project.Services {
for name, service := range project.Services {
if service.Name != requestedServiceName {
dependencies = append(dependencies, service)
dependencies[name] = service
} else {
requestedService = service
}
}
project.Services = dependencies
project.DisabledServices = append(project.DisabledServices, requestedService)
project.DisabledServices[requestedServiceName] = requestedService
err := backend.Create(ctx, &project, api.CreateOptions{
Build: buildOpts,
IgnoreOrphans: ignoreOrphans,

View File

@ -26,10 +26,10 @@ import (
func TestApplyScaleOpt(t *testing.T) {
p := types.Project{
Services: types.Services{
{
"foo": {
Name: "foo",
},
{
"bar": {
Name: "bar",
Deploy: &types.DeployConfig{
Mode: "test",

2
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/Microsoft/go-winio v0.6.1
github.com/buger/goterm v1.0.4
github.com/compose-spec/compose-go/v2 v2.0.0-20231121074112-593b77722992
github.com/compose-spec/compose-go/v2 v2.0.0-20231123162526-11ef9572f1a4
github.com/containerd/console v1.0.3
github.com/containerd/containerd v1.7.7
github.com/davecgh/go-spew v1.1.1

4
go.sum
View File

@ -132,8 +132,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.0.0-20231121074112-593b77722992 h1:0BM7GPtSRK7djjvG3h67aJYH8eRikBgxkrEG7wNtgaU=
github.com/compose-spec/compose-go/v2 v2.0.0-20231121074112-593b77722992/go.mod h1:uAthZuC/GWStR8mxGMRaQyaOeSqA4V+MZIiAIfuBoIU=
github.com/compose-spec/compose-go/v2 v2.0.0-20231123162526-11ef9572f1a4 h1:Lr78By808iuG+2gTyxIDslRpKQCk/lcRqElKsrhzp+U=
github.com/compose-spec/compose-go/v2 v2.0.0-20231123162526-11ef9572f1a4/go.mod h1:PWCgeD8cxiI/DmdpBM407CuLDrZ2W4xuS6/Z9jAi0YQ=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=

View File

@ -58,17 +58,12 @@ func ProjectOptions(proj *types.Project) SpanOptions {
return nil
}
disabledServiceNames := make([]string, len(proj.DisabledServices))
for i := range proj.DisabledServices {
disabledServiceNames[i] = proj.DisabledServices[i].Name
}
attrs := []attribute.KeyValue{
attribute.String("project.name", proj.Name),
attribute.String("project.dir", proj.WorkingDir),
attribute.StringSlice("project.compose_files", proj.ComposeFiles),
attribute.StringSlice("project.services.active", proj.ServiceNames()),
attribute.StringSlice("project.services.disabled", disabledServiceNames),
attribute.StringSlice("project.services.disabled", proj.DisabledServiceNames()),
attribute.StringSlice("project.profiles", proj.Profiles),
attribute.StringSlice("project.volumes", proj.VolumeNames()),
attribute.StringSlice("project.networks", proj.NetworkNames()),

View File

@ -67,7 +67,7 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
}
type serviceToBuild struct {
idx int
name string
service types.ServiceConfig
}
@ -85,7 +85,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
if len(options.Services) > 0 && !utils.Contains(options.Services, name) {
return nil
}
service, idx := getServiceIndex(project, name)
service := project.Services[name]
if service.Build == nil {
return nil
@ -97,7 +97,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
return nil
}
mapServiceMutx.Lock()
serviceToBeBuild[name] = serviceToBuild{idx: idx, service: service}
serviceToBeBuild[name] = serviceToBuild{name: name, service: service}
mapServiceMutx.Unlock()
return nil
}, func(traversal *graphTraversal) {
@ -146,7 +146,17 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
}
}
// we use a pre-allocated []string to collect build digest by service index while running concurrent goroutines
builtDigests := make([]string, len(project.Services))
names := project.ServiceNames()
getServiceIndex := func(name string) int {
for idx, n := range names {
if n == name {
return idx
}
}
return -1
}
err = InDependencyOrder(ctx, project, func(ctx context.Context, name string) error {
if len(options.Services) > 0 && !utils.Contains(options.Services, name) {
return nil
@ -156,14 +166,13 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
return nil
}
service := serviceToBuild.service
idx := serviceToBuild.idx
if !buildkitEnabled {
id, err := s.doBuildClassic(ctx, project, service, options)
if err != nil {
return err
}
builtDigests[idx] = id
builtDigests[getServiceIndex(name)] = id
if options.Push {
return s.push(ctx, project, api.PushOptions{})
@ -184,7 +193,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
if err != nil {
return err
}
builtDigests[idx] = digest
builtDigests[getServiceIndex(name)] = digest
return nil
}, func(traversal *graphTraversal) {
@ -204,25 +213,13 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
for i, imageDigest := range builtDigests {
if imageDigest != "" {
imageRef := api.GetImageNameOrDefault(project.Services[i], project.Name)
imageRef := api.GetImageNameOrDefault(project.Services[names[i]], project.Name)
imageIDs[imageRef] = imageDigest
}
}
return imageIDs, err
}
func getServiceIndex(project *types.Project, name string) (types.ServiceConfig, int) {
var service types.ServiceConfig
var idx int
for i, s := range project.Services {
if s.Name == name {
idx, service = i, s
break
}
}
return service, idx
}
func (s *composeService) ensureImagesExists(ctx context.Context, project *types.Project, buildOpts *api.BuildOptions, quietPull bool) error {
for _, service := range project.Services {
if service.Image == "" && service.Build == nil {
@ -264,14 +261,14 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
}
// set digest as com.docker.compose.image label so we can detect outdated containers
for i, service := range project.Services {
for _, service := range project.Services {
image := api.GetImageNameOrDefault(service, project.Name)
digest, ok := images[image]
if ok {
if project.Services[i].Labels == nil {
project.Services[i].Labels = types.Labels{}
if service.Labels == nil {
service.Labels = types.Labels{}
}
project.Services[i].CustomLabels.Add(api.ImageDigestLabel, digest)
service.CustomLabels.Add(api.ImageDigestLabel, digest)
}
}
return nil
@ -440,7 +437,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
Platforms: plats,
Labels: imageLabels,
NetworkMode: service.Build.Network,
ExtraHosts: service.Build.ExtraHosts.AsList(),
ExtraHosts: service.Build.ExtraHosts.AsList(":"),
Ulimits: toUlimitOpt(service.Build.Ulimits),
Session: sessionConfig,
Allow: allow,

View File

@ -229,7 +229,7 @@ func imageBuildOptions(dockerCli command.Cli, project *types.Project, service ty
BuildArgs: resolveAndMergeBuildArgs(dockerCli, project, service, options),
Labels: config.Labels,
NetworkMode: config.Network,
ExtraHosts: config.ExtraHosts.AsList(),
ExtraHosts: config.ExtraHosts.AsList(":"),
Target: config.Target,
Isolation: container.Isolation(config.Isolation),
}

View File

@ -183,7 +183,7 @@ func (s *composeService) projectFromName(containers Containers, projectName stri
if len(containers) == 0 {
return project, fmt.Errorf("no container found for project %q: %w", projectName, api.ErrNotFound)
}
set := map[string]types.ServiceConfig{}
set := types.Services{}
for _, c := range containers {
serviceLabel := c.Labels[api.ServiceLabel]
service, ok := set[serviceLabel]
@ -197,7 +197,7 @@ func (s *composeService) projectFromName(containers Containers, projectName stri
}
service.Scale = increment(service.Scale)
}
for _, service := range set {
for name, service := range set {
dependencies := service.Labels[api.DependenciesLabel]
if len(dependencies) > 0 {
service.DependsOn = types.DependsOnConfig{}
@ -218,9 +218,11 @@ func (s *composeService) projectFromName(containers Containers, projectName stri
}
service.DependsOn[dependency] = types.ServiceDependency{Condition: condition, Restart: restart, Required: required}
}
set[name] = service
}
project.Services = append(project.Services, service)
}
project.Services = set
SERVICES:
for _, qs := range services {
for _, es := range project.Services {

View File

@ -227,7 +227,10 @@ func TestWaitDependencies(t *testing.T) {
t.Run("should skip dependencies with scale 0", func(t *testing.T) {
dbService := types.ServiceConfig{Name: "db", Scale: intPtr(0)}
redisService := types.ServiceConfig{Name: "redis", Scale: intPtr(0)}
project := types.Project{Name: strings.ToLower(testProject), Services: types.Services{dbService, redisService}}
project := types.Project{Name: strings.ToLower(testProject), Services: types.Services{
"db": dbService,
"redis": redisService,
}}
dependencies := types.DependsOnConfig{
"db": {Condition: ServiceConditionRunningOrHealthy},
"redis": {Condition: ServiceConditionRunningOrHealthy},
@ -237,7 +240,10 @@ func TestWaitDependencies(t *testing.T) {
t.Run("should skip dependencies with condition service_started", func(t *testing.T) {
dbService := types.ServiceConfig{Name: "db", Scale: intPtr(1)}
redisService := types.ServiceConfig{Name: "redis", Scale: intPtr(1)}
project := types.Project{Name: strings.ToLower(testProject), Services: types.Services{dbService, redisService}}
project := types.Project{Name: strings.ToLower(testProject), Services: types.Services{
"db": dbService,
"redis": redisService,
}}
dependencies := types.DependsOnConfig{
"db": {Condition: types.ServiceConditionStarted, Required: true},
"redis": {Condition: types.ServiceConditionStarted, Required: true},

View File

@ -94,11 +94,7 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt
return err
}
allServices := project.AllServices()
allServiceNames := []string{}
for _, service := range allServices {
allServiceNames = append(allServiceNames, service.Name)
}
allServiceNames := append(project.ServiceNames(), project.DisabledServiceNames()...)
orphans := observedState.filter(isNotService(allServiceNames...))
if len(orphans) > 0 && !options.IgnoreOrphans {
if options.RemoveOrphans {
@ -263,7 +259,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
DNS: service.DNS,
DNSSearch: service.DNSSearch,
DNSOptions: service.DNSOpts,
ExtraHosts: service.ExtraHosts.AsList(),
ExtraHosts: service.ExtraHosts.AsList(":"),
SecurityOpt: securityOpts,
UsernsMode: container.UsernsMode(service.UserNSMode),
UTSMode: container.UTSMode(service.Uts),

View File

@ -101,8 +101,8 @@ func TestPrepareNetworkLabels(t *testing.T) {
func TestBuildContainerMountOptions(t *testing.T) {
project := composetypes.Project{
Name: "myProject",
Services: []composetypes.ServiceConfig{
{
Services: composetypes.Services{
"myService": {
Name: "myService",
Volumes: []composetypes.ServiceVolumeConfig{
{
@ -144,7 +144,7 @@ func TestBuildContainerMountOptions(t *testing.T) {
},
}
mounts, err := buildContainerMountOptions(project, project.Services[0], moby.ImageInspect{}, inherit)
mounts, err := buildContainerMountOptions(project, project.Services["myService"], moby.ImageInspect{}, inherit)
sort.Slice(mounts, func(i, j int) bool {
return mounts[i].Target < mounts[j].Target
})
@ -154,7 +154,7 @@ func TestBuildContainerMountOptions(t *testing.T) {
assert.Equal(t, mounts[1].Target, "/var/myvolume2")
assert.Equal(t, mounts[2].Target, "\\\\.\\pipe\\docker_engine")
mounts, err = buildContainerMountOptions(project, project.Services[0], moby.ImageInspect{}, inherit)
mounts, err = buildContainerMountOptions(project, project.Services["myService"], moby.ImageInspect{}, inherit)
sort.Slice(mounts, func(i, j int) bool {
return mounts[i].Target < mounts[j].Target
})
@ -180,8 +180,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
}
project := composetypes.Project{
Name: "myProject",
Services: []composetypes.ServiceConfig{
service,
Services: composetypes.Services{
"myService": service,
},
Networks: composetypes.Networks(map[string]composetypes.NetworkConfig{
"myNetwork1": {
@ -205,8 +205,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
}
project := composetypes.Project{
Name: "myProject",
Services: []composetypes.ServiceConfig{
service,
Services: composetypes.Services{
"myService": service,
},
Networks: composetypes.Networks(map[string]composetypes.NetworkConfig{
"myNetwork1": {
@ -233,8 +233,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
}
project := composetypes.Project{
Name: "myProject",
Services: []composetypes.ServiceConfig{
service,
Services: composetypes.Services{
"myService": service,
},
}
@ -250,7 +250,7 @@ func TestDefaultNetworkSettings(t *testing.T) {
}
project := composetypes.Project{
Name: "myProject",
Services: []composetypes.ServiceConfig{service},
Services: composetypes.Services{"myService": service},
Networks: composetypes.Networks(map[string]composetypes.NetworkConfig{
"default": {
Name: "myProject_default",

View File

@ -33,19 +33,19 @@ import (
func createTestProject() *types.Project {
return &types.Project{
Services: types.Services{
{
"test1": {
Name: "test1",
DependsOn: map[string]types.ServiceDependency{
"test2": {},
},
},
{
"test2": {
Name: "test2",
DependsOn: map[string]types.ServiceDependency{
"test3": {},
},
},
{
"test3": {
Name: "test3",
},
},
@ -59,7 +59,7 @@ func TestTraversalWithMultipleParents(t *testing.T) {
}
project := types.Project{
Services: types.Services{dependent},
Services: types.Services{"dependent": dependent},
}
for i := 1; i <= 100; i++ {
@ -67,7 +67,7 @@ func TestTraversalWithMultipleParents(t *testing.T) {
dependent.DependsOn[name] = types.ServiceDependency{}
svc := types.ServiceConfig{Name: name}
project.Services = append(project.Services, svc)
project.Services[name] = svc
}
ctx, cancel := context.WithCancel(context.Background())
@ -132,7 +132,7 @@ func TestBuildGraph(t *testing.T) {
{
desc: "builds graph with single service",
services: types.Services{
{
"test": {
Name: "test",
DependsOn: types.DependsOnConfig{},
},
@ -150,11 +150,11 @@ func TestBuildGraph(t *testing.T) {
{
desc: "builds graph with two separate services",
services: types.Services{
{
"test": {
Name: "test",
DependsOn: types.DependsOnConfig{},
},
{
"another": {
Name: "another",
DependsOn: types.DependsOnConfig{},
},
@ -179,13 +179,13 @@ func TestBuildGraph(t *testing.T) {
{
desc: "builds graph with a service and a dependency",
services: types.Services{
{
"test": {
Name: "test",
DependsOn: types.DependsOnConfig{
"another": types.ServiceDependency{},
},
},
{
"another": {
Name: "another",
DependsOn: types.DependsOnConfig{},
},
@ -214,19 +214,19 @@ func TestBuildGraph(t *testing.T) {
{
desc: "builds graph with multiple dependency levels",
services: types.Services{
{
"test": {
Name: "test",
DependsOn: types.DependsOnConfig{
"another": types.ServiceDependency{},
},
},
{
"another": {
Name: "another",
DependsOn: types.DependsOnConfig{
"another_dep": types.ServiceDependency{},
},
},
{
"another_dep": {
Name: "another_dep",
DependsOn: types.DependsOnConfig{},
},

View File

@ -184,12 +184,12 @@ func TestDownRemoveImages(t *testing.T) {
Project: &types.Project{
Name: strings.ToLower(testProject),
Services: types.Services{
{Name: "local-anonymous"},
{Name: "local-named", Image: "local-named-image"},
{Name: "remote", Image: "remote-image"},
{Name: "remote-tagged", Image: "registry.example.com/remote-image-tagged:v1.0"},
{Name: "no-images-anonymous"},
{Name: "no-images-named", Image: "missing-named-image"},
"local-anonymous": {Name: "local-anonymous"},
"local-named": {Name: "local-named", Image: "local-named-image"},
"remote": {Name: "remote", Image: "remote-image"},
"remote-tagged": {Name: "remote-tagged", Image: "registry.example.com/remote-image-tagged:v1.0"},
"no-images-anonymous": {Name: "no-images-anonymous"},
"no-images-named": {Name: "no-images-named", Image: "missing-named-image"},
},
},
}

View File

@ -83,7 +83,7 @@ func TestComposeService_Logs_Demux(t *testing.T) {
opts := compose.LogOptions{
Project: &types.Project{
Services: types.Services{
{Name: "service"},
"service": {Name: "service"},
},
},
}
@ -153,8 +153,8 @@ func TestComposeService_Logs_ServiceFiltering(t *testing.T) {
// reference `serviceB` even though it has running services for this proj
proj := &types.Project{
Services: types.Services{
{Name: "serviceA"},
{Name: "serviceC"},
"serviceA": {Name: "serviceA"},
"serviceC": {Name: "serviceC"},
},
}
consumer := &testLogConsumer{}

View File

@ -63,8 +63,8 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
imagesBeingPulled = map[string]string{}
)
for i, service := range project.Services {
i, service := i, service
i := 0
for _, service := range project.Services {
if service.Image == "" {
w.Event(progress.Event{
ID: service.Name,
@ -113,10 +113,11 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
imagesBeingPulled[service.Image] = service.Name
idx, service := i, service
eg.Go(func() error {
_, err := s.pullServiceImage(ctx, service, s.configFile(), w, false, project.Environment["DOCKER_DEFAULT_PLATFORM"])
if err != nil {
pullErrors[i] = err
pullErrors[idx] = err
if service.Build != nil {
mustBuild = append(mustBuild, service.Name)
}
@ -134,6 +135,7 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
}
return nil
})
i++
}
err = eg.Wait()
@ -260,7 +262,7 @@ func encodedAuth(ref reference.Named, configFile driver.Auth) (string, error) {
}
func (s *composeService) pullRequiredImages(ctx context.Context, project *types.Project, images map[string]string, quietPull bool) error {
var needPull types.Services
var needPull []types.ServiceConfig
for _, service := range project.Services {
if service.Image == "" {
continue

View File

@ -34,7 +34,7 @@ func TestViz(t *testing.T) {
Name: "viz-test",
WorkingDir: "/home",
Services: types.Services{
{
"service1": {
Name: "service1",
Image: "image-for-service1",
Ports: []types.ServicePortConfig{
@ -53,12 +53,12 @@ func TestViz(t *testing.T) {
"internal": nil,
},
},
{
"service2": {
Name: "service2",
Image: "image-for-service2",
Ports: []types.ServicePortConfig{},
},
{
"service3": {
Name: "service3",
Image: "some-image",
DependsOn: map[string]types.ServiceDependency{
@ -66,7 +66,7 @@ func TestViz(t *testing.T) {
"service1": {},
},
},
{
"service4": {
Name: "service4",
Image: "another-image",
DependsOn: map[string]types.ServiceDependency{
@ -82,7 +82,7 @@ func TestViz(t *testing.T) {
"external": nil,
},
},
{
"With host IP": {
Name: "With host IP",
Image: "user/image-name",
DependsOn: map[string]types.ServiceDependency{

View File

@ -106,7 +106,7 @@ func TestWatch_Sync(t *testing.T) {
proj := types.Project{
Services: types.Services{
{
"test": {
Name: "test",
},
},

View File

@ -79,10 +79,10 @@ func TestRestartWithDependencies(t *testing.T) {
c.RunDockerComposeCmd(t, "-f", "./fixtures/restart-test/compose-depends-on.yaml", "up", "-d")
res := c.RunDockerComposeCmd(t, "restart", baseService)
fmt.Println(res.Combined())
assert.Assert(t, strings.Contains(res.Combined(), fmt.Sprintf("Container e2e-restart-deps-%s-1 Started", baseService)), res.Combined())
assert.Assert(t, strings.Contains(res.Combined(), fmt.Sprintf("Container e2e-restart-deps-%s-1 Started", depWithRestart)), res.Combined())
assert.Assert(t, !strings.Contains(res.Combined(), depNoRestart), res.Combined())
out := res.Combined()
assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Started", baseService)), out)
assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Started", depWithRestart)), out)
assert.Assert(t, !strings.Contains(out, depNoRestart), out)
}
func TestRestartWithProfiles(t *testing.T) {

View File

@ -106,12 +106,14 @@ func TestStartStopWithDependencies(t *testing.T) {
assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1 Stopped"), res.Combined())
res = c.RunDockerComposeCmd(t, "--project-name", projectName, "start", "foo")
assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1 Started"), res.Combined())
assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1 Started"), res.Combined())
out := res.Combined()
assert.Assert(t, strings.Contains(out, "Container e2e-start-stop-with-dependencies-bar-1 Started"), out)
assert.Assert(t, strings.Contains(out, "Container e2e-start-stop-with-dependencies-foo-1 Started"), out)
res = c.RunDockerComposeCmd(t, "--project-name", projectName, "ps", "--status", "running")
assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-bar-1"), res.Combined())
assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-foo-1"), res.Combined())
out = res.Combined()
assert.Assert(t, strings.Contains(out, "e2e-start-stop-with-dependencies-bar-1"), out)
assert.Assert(t, strings.Contains(out, "e2e-start-stop-with-dependencies-foo-1"), out)
})
t.Run("Up no-deps links", func(t *testing.T) {