From 138facea624ed02037bdeeed37ed59dbe453e08e Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Mon, 27 Nov 2023 10:14:31 +0100 Subject: [PATCH] project.Services is a map Signed-off-by: Nicolas De Loof --- cmd/compose/build.go | 1 - cmd/compose/compose_test.go | 8 +++--- cmd/compose/create_test.go | 2 +- cmd/compose/options.go | 5 +--- cmd/compose/options_test.go | 14 +++++----- cmd/compose/pullOptions_test.go | 12 ++++----- cmd/compose/run.go | 6 ++--- cmd/compose/up_test.go | 4 +-- go.mod | 2 +- go.sum | 4 +-- internal/tracing/attributes.go | 7 +---- pkg/compose/build.go | 45 +++++++++++++++----------------- pkg/compose/build_classic.go | 2 +- pkg/compose/compose.go | 8 +++--- pkg/compose/convergence_test.go | 10 +++++-- pkg/compose/create.go | 8 ++---- pkg/compose/create_test.go | 22 ++++++++-------- pkg/compose/dependencies_test.go | 26 +++++++++--------- pkg/compose/down_test.go | 12 ++++----- pkg/compose/logs_test.go | 6 ++--- pkg/compose/pull.go | 10 ++++--- pkg/compose/viz_test.go | 10 +++---- pkg/compose/watch_test.go | 2 +- pkg/e2e/restart_test.go | 8 +++--- pkg/e2e/start_stop_test.go | 10 ++++--- 25 files changed, 120 insertions(+), 124 deletions(-) diff --git a/cmd/compose/build.go b/cmd/compose/build.go index f0e26d34a..76c7fc814 100644 --- a/cmd/compose/build.go +++ b/cmd/compose/build.go @@ -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" diff --git a/cmd/compose/compose_test.go b/cmd/compose/compose_test.go index f6626a6a9..b61e6215f 100644 --- a/cmd/compose/compose_test.go +++ b/cmd/compose/compose_test.go @@ -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", }, }, diff --git a/cmd/compose/create_test.go b/cmd/compose/create_test.go index eecd6d9c0..77a6e9e13 100644 --- a/cmd/compose/create_test.go +++ b/cmd/compose/create_test.go @@ -86,7 +86,7 @@ func sampleProject() *types.Project { return &types.Project{ Name: "test", Services: types.Services{ - { + "svc": { Name: "svc", Build: &types.BuildConfig{ Context: ".", diff --git a/cmd/compose/options.go b/cmd/compose/options.go index 988fa5f1d..3552841a1 100644 --- a/cmd/compose/options.go +++ b/cmd/compose/options.go @@ -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 } diff --git a/cmd/compose/options_test.go b/cmd/compose/options_test.go index 44c602455..28b34298a 100644 --- a/cmd/compose/options_test.go +++ b/cmd/compose/options_test.go @@ -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{ diff --git a/cmd/compose/pullOptions_test.go b/cmd/compose/pullOptions_test.go index 2e68d91a9..153a050ec 100644 --- a/cmd/compose/pullOptions_test.go +++ b/cmd/compose/pullOptions_test.go @@ -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) } diff --git a/cmd/compose/run.go b/cmd/compose/run.go index 6c8c12740..e252315fd 100644 --- a/cmd/compose/run.go +++ b/cmd/compose/run.go @@ -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, diff --git a/cmd/compose/up_test.go b/cmd/compose/up_test.go index 294527841..cf41d2d8f 100644 --- a/cmd/compose/up_test.go +++ b/cmd/compose/up_test.go @@ -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", diff --git a/go.mod b/go.mod index 5d20b5d1b..5e30d7e44 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 48bf3a4ba..b539794ea 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/tracing/attributes.go b/internal/tracing/attributes.go index ea03e2195..1fd2dd7f9 100644 --- a/internal/tracing/attributes.go +++ b/internal/tracing/attributes.go @@ -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()), diff --git a/pkg/compose/build.go b/pkg/compose/build.go index 9684ea51b..1b356007b 100644 --- a/pkg/compose/build.go +++ b/pkg/compose/build.go @@ -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, diff --git a/pkg/compose/build_classic.go b/pkg/compose/build_classic.go index 84a2c8e2d..150383e65 100644 --- a/pkg/compose/build_classic.go +++ b/pkg/compose/build_classic.go @@ -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), } diff --git a/pkg/compose/compose.go b/pkg/compose/compose.go index b4f17d222..624d2f499 100644 --- a/pkg/compose/compose.go +++ b/pkg/compose/compose.go @@ -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 { diff --git a/pkg/compose/convergence_test.go b/pkg/compose/convergence_test.go index 0ca12cfcb..eca2e5006 100644 --- a/pkg/compose/convergence_test.go +++ b/pkg/compose/convergence_test.go @@ -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}, diff --git a/pkg/compose/create.go b/pkg/compose/create.go index 9d4f48ebc..ddeff661d 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -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), diff --git a/pkg/compose/create_test.go b/pkg/compose/create_test.go index 4d2bcec7f..1a4734896 100644 --- a/pkg/compose/create_test.go +++ b/pkg/compose/create_test.go @@ -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", diff --git a/pkg/compose/dependencies_test.go b/pkg/compose/dependencies_test.go index 8b25c3b98..b54702fb3 100644 --- a/pkg/compose/dependencies_test.go +++ b/pkg/compose/dependencies_test.go @@ -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{}, }, diff --git a/pkg/compose/down_test.go b/pkg/compose/down_test.go index df2c87371..08f3dfbfb 100644 --- a/pkg/compose/down_test.go +++ b/pkg/compose/down_test.go @@ -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"}, }, }, } diff --git a/pkg/compose/logs_test.go b/pkg/compose/logs_test.go index d60d7c401..dad51f0ea 100644 --- a/pkg/compose/logs_test.go +++ b/pkg/compose/logs_test.go @@ -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{} diff --git a/pkg/compose/pull.go b/pkg/compose/pull.go index be9f83e19..4b1f912d7 100644 --- a/pkg/compose/pull.go +++ b/pkg/compose/pull.go @@ -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 diff --git a/pkg/compose/viz_test.go b/pkg/compose/viz_test.go index 26cc6185c..78cbd3458 100644 --- a/pkg/compose/viz_test.go +++ b/pkg/compose/viz_test.go @@ -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{ diff --git a/pkg/compose/watch_test.go b/pkg/compose/watch_test.go index 2c9511067..c444182d3 100644 --- a/pkg/compose/watch_test.go +++ b/pkg/compose/watch_test.go @@ -106,7 +106,7 @@ func TestWatch_Sync(t *testing.T) { proj := types.Project{ Services: types.Services{ - { + "test": { Name: "test", }, }, diff --git a/pkg/e2e/restart_test.go b/pkg/e2e/restart_test.go index 91ade0b30..2b2a319f8 100644 --- a/pkg/e2e/restart_test.go +++ b/pkg/e2e/restart_test.go @@ -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) { diff --git a/pkg/e2e/start_stop_test.go b/pkg/e2e/start_stop_test.go index 16730c3cc..75d9298b2 100644 --- a/pkg/e2e/start_stop_test.go +++ b/pkg/e2e/start_stop_test.go @@ -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) {