From abf5f736c7d66f259e6bb61dd1be653d71c23e05 Mon Sep 17 00:00:00 2001
From: Nicolas De Loof <nicolas.deloof@gmail.com>
Date: Wed, 16 Feb 2022 15:51:10 +0100
Subject: [PATCH] as --no-build is set, set service image to default image name

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Signed-off-by: Guillaume Lours <guillaume.lours@docker.com>
---
 cmd/compose/create.go        |  3 +++
 pkg/api/api.go               |  9 +++++++++
 pkg/compose/build.go         | 10 +++++-----
 pkg/compose/build_classic.go |  3 ++-
 pkg/compose/create.go        | 12 ++----------
 pkg/compose/create_test.go   |  8 +++++---
 pkg/compose/down.go          |  2 +-
 7 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/cmd/compose/create.go b/cmd/compose/create.go
index 717c28b31..50e7a6f79 100644
--- a/cmd/compose/create.go
+++ b/cmd/compose/create.go
@@ -126,6 +126,9 @@ func (opts createOptions) Apply(project *types.Project) {
 	if opts.noBuild {
 		for i, service := range project.Services {
 			service.Build = nil
+			if service.Image == "" {
+				service.Image = api.GetImageNameOrDefault(service, project.Name)
+			}
 			project.Services[i] = service
 		}
 	}
diff --git a/pkg/api/api.go b/pkg/api/api.go
index 08f6433d8..43f6745a6 100644
--- a/pkg/api/api.go
+++ b/pkg/api/api.go
@@ -445,3 +445,12 @@ const (
 	// UserCancel user cancelled compose up, we are stopping containers
 	UserCancel
 )
+
+// GetImageNameOrDefault computes the default image name for a service, used to tag built images
+func GetImageNameOrDefault(service types.ServiceConfig, projectName string) string {
+	imageName := service.Image
+	if imageName == "" {
+		imageName = projectName + "_" + service.Name
+	}
+	return imageName
+}
diff --git a/pkg/compose/build.go b/pkg/compose/build.go
index 7581a664d..937bfd361 100644
--- a/pkg/compose/build.go
+++ b/pkg/compose/build.go
@@ -64,7 +64,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
 		if service.Build == nil {
 			continue
 		}
-		imageName := getImageName(service, project.Name)
+		imageName := api.GetImageNameOrDefault(service, project.Name)
 		imagesToBuild = append(imagesToBuild, imageName)
 		buildOptions, err := s.toBuildOptions(project, service, imageName, options.SSHs)
 		if err != nil {
@@ -135,7 +135,7 @@ 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 {
-		image := getImageName(service, project.Name)
+		image := api.GetImageNameOrDefault(service, project.Name)
 		digest, ok := images[image]
 		if ok {
 			if project.Services[i].Labels == nil {
@@ -154,7 +154,7 @@ func (s *composeService) getBuildOptions(project *types.Project, images map[stri
 		if service.Image == "" && service.Build == nil {
 			return nil, fmt.Errorf("invalid service %q. Must specify either image or build", service.Name)
 		}
-		imageName := getImageName(service, project.Name)
+		imageName := api.GetImageNameOrDefault(service, project.Name)
 		_, localImagePresent := images[imageName]
 
 		if service.Build != nil {
@@ -176,7 +176,7 @@ func (s *composeService) getBuildOptions(project *types.Project, images map[stri
 func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]string, error) {
 	var imageNames []string
 	for _, s := range project.Services {
-		imgName := getImageName(s, project.Name)
+		imgName := api.GetImageNameOrDefault(s, project.Name)
 		if !utils.StringContains(imageNames, imgName) {
 			imageNames = append(imageNames, imgName)
 		}
@@ -191,7 +191,7 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
 	}
 
 	for i := range project.Services {
-		imgName := getImageName(project.Services[i], project.Name)
+		imgName := api.GetImageNameOrDefault(project.Services[i], project.Name)
 		digest, ok := images[imgName]
 		if ok {
 			project.Services[i].CustomLabels.Add(api.ImageDigestLabel, digest)
diff --git a/pkg/compose/build_classic.go b/pkg/compose/build_classic.go
index db5d948e3..dc88dee3f 100644
--- a/pkg/compose/build_classic.go
+++ b/pkg/compose/build_classic.go
@@ -29,6 +29,7 @@ import (
 	"github.com/compose-spec/compose-go/types"
 	buildx "github.com/docker/buildx/build"
 	"github.com/docker/cli/cli/command/image/build"
+	"github.com/docker/compose/v2/pkg/api"
 	dockertypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/archive"
@@ -45,7 +46,7 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
 	var nameDigests = make(map[string]string)
 	var errs error
 	err := project.WithServices(nil, func(service types.ServiceConfig) error {
-		imageName := getImageName(service, project.Name)
+		imageName := api.GetImageNameOrDefault(service, project.Name)
 		o, ok := opts[imageName]
 		if !ok {
 			return nil
diff --git a/pkg/compose/create.go b/pkg/compose/create.go
index e0789d1fb..2b0d0b841 100644
--- a/pkg/compose/create.go
+++ b/pkg/compose/create.go
@@ -227,14 +227,6 @@ func (s *composeService) ensureProjectVolumes(ctx context.Context, project *type
 	return nil
 }
 
-func getImageName(service types.ServiceConfig, projectName string) string {
-	imageName := service.Image
-	if imageName == "" {
-		imageName = projectName + "_" + service.Name
-	}
-	return imageName
-}
-
 func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, service types.ServiceConfig,
 	number int, inherit *moby.Container, autoRemove bool, attachStdin bool) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
 
@@ -279,7 +271,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
 		AttachStderr:    true,
 		AttachStdout:    true,
 		Cmd:             runCmd,
-		Image:           getImageName(service, p.Name),
+		Image:           api.GetImageNameOrDefault(service, p.Name),
 		WorkingDir:      service.WorkingDir,
 		Entrypoint:      entrypoint,
 		NetworkDisabled: service.NetworkMode == "disabled",
@@ -712,7 +704,7 @@ func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Proj
 	inherit *moby.Container) (map[string]struct{}, []string, []mount.Mount, error) {
 	var mounts = []mount.Mount{}
 
-	image := getImageName(service, p.Name)
+	image := api.GetImageNameOrDefault(service, p.Name)
 	imgInspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, image)
 	if err != nil {
 		return nil, nil, nil, err
diff --git a/pkg/compose/create_test.go b/pkg/compose/create_test.go
index 7a0e95c6f..dceed1361 100644
--- a/pkg/compose/create_test.go
+++ b/pkg/compose/create_test.go
@@ -22,10 +22,12 @@ import (
 	"sort"
 	"testing"
 
-	composetypes "github.com/compose-spec/compose-go/types"
 	"github.com/docker/compose/v2/pkg/api"
+
+	composetypes "github.com/compose-spec/compose-go/types"
 	moby "github.com/docker/docker/api/types"
 	mountTypes "github.com/docker/docker/api/types/mount"
+
 	"gotest.tools/v3/assert"
 )
 
@@ -65,8 +67,8 @@ func TestBuildVolumeMount(t *testing.T) {
 }
 
 func TestServiceImageName(t *testing.T) {
-	assert.Equal(t, getImageName(composetypes.ServiceConfig{Image: "myImage"}, "myProject"), "myImage")
-	assert.Equal(t, getImageName(composetypes.ServiceConfig{Name: "aService"}, "myProject"), "myProject_aService")
+	assert.Equal(t, api.GetImageNameOrDefault(composetypes.ServiceConfig{Image: "myImage"}, "myProject"), "myImage")
+	assert.Equal(t, api.GetImageNameOrDefault(composetypes.ServiceConfig{Name: "aService"}, "myProject"), "myProject_aService")
 }
 
 func TestPrepareNetworkLabels(t *testing.T) {
diff --git a/pkg/compose/down.go b/pkg/compose/down.go
index 1ea7b4dcb..4251a8dc1 100644
--- a/pkg/compose/down.go
+++ b/pkg/compose/down.go
@@ -195,7 +195,7 @@ func (s *composeService) getServiceImages(options api.DownOptions, project *type
 			continue
 		}
 		if image == "" {
-			image = getImageName(service, project.Name)
+			image = api.GetImageNameOrDefault(service, project.Name)
 		}
 		images[image] = struct{}{}
 	}