Skip to content

Commit 636c13f

Browse files
milasglours
authored andcommitted
build: do not attempt to push unnamed service images
When building, if images are being pushed, ensure that only named images (i.e. services with a populated `image` field) are attempted to be pushed. Services without `image` get an auto-generated name, which will be a "Docker library" reference since they're in the format `$project-$service`, which is implicitly the same as `docker.io/library/$project-$service`. A push for that is never desirable / will always fail. The key here is that we cannot overwrite the `<svc>.image` field when doing builds, as we need to be able to check for its presence to determine whether a push makes sense. Fixes docker#10813. Signed-off-by: Milas Bowman <[email protected]>
1 parent 5a072b1 commit 636c13f

File tree

7 files changed

+36
-26
lines changed

7 files changed

+36
-26
lines changed

cmd/compose/config.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,7 @@ func runConfigImages(streams api.Streams, opts configOptions, services []string)
233233
return err
234234
}
235235
for _, s := range project.Services {
236-
if s.Image != "" {
237-
fmt.Fprintln(streams.Out(), s.Image)
238-
} else {
239-
fmt.Fprintf(streams.Out(), "%s%s%s\n", project.Name, api.Separator, s.Name)
240-
}
236+
fmt.Fprintln(streams.Out(), api.GetImageNameOrDefault(s, project.Name))
241237
}
242238
return nil
243239
}

pkg/api/api.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ func (o BuildOptions) Apply(project *types.Project) error {
145145
if service.Build == nil {
146146
continue
147147
}
148-
service.Image = GetImageNameOrDefault(service, project.Name)
149148
if platform != "" {
150149
if len(service.Build.Platforms) > 0 && !utils.StringContains(service.Build.Platforms, platform) {
151150
return fmt.Errorf("service %q build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: %s", service.Name, platform)

pkg/compose/build.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
7777
return nil, err
7878
}
7979

80-
builtIDs := make([]string, len(project.Services))
80+
builtDigests := make([]string, len(project.Services))
8181
err = InDependencyOrder(ctx, project, func(ctx context.Context, name string) error {
8282
if len(options.Services) > 0 && !utils.Contains(options.Services, name) {
8383
return nil
@@ -94,11 +94,11 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
9494
} else {
9595
service.Build.Args = service.Build.Args.OverrideBy(args)
9696
}
97-
id, err := s.doBuildClassic(ctx, service, options)
97+
id, err := s.doBuildClassic(ctx, project.Name, service, options)
9898
if err != nil {
9999
return err
100100
}
101-
builtIDs[idx] = id
101+
builtDigests[idx] = id
102102

103103
if options.Push {
104104
return s.push(ctx, project, api.PushOptions{})
@@ -120,7 +120,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
120120
if err != nil {
121121
return err
122122
}
123-
builtIDs[idx] = digest
123+
builtDigests[idx] = digest
124124

125125
return nil
126126
}, func(traversal *graphTraversal) {
@@ -137,9 +137,10 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
137137
}
138138

139139
imageIDs := map[string]string{}
140-
for i, d := range builtIDs {
141-
if d != "" {
142-
imageIDs[project.Services[i].Image] = d
140+
for i, imageDigest := range builtDigests {
141+
if imageDigest != "" {
142+
imageRef := api.GetImageNameOrDefault(project.Services[i], project.Name)
143+
imageIDs[imageRef] = imageDigest
143144
}
144145
}
145146
return imageIDs, err
@@ -222,7 +223,8 @@ func (s *composeService) prepareProjectForBuild(project *types.Project, images m
222223
continue
223224
}
224225

225-
_, localImagePresent := images[service.Image]
226+
image := api.GetImageNameOrDefault(service, project.Name)
227+
_, localImagePresent := images[image]
226228
if localImagePresent && service.PullPolicy != types.PullPolicyBuild {
227229
service.Build = nil
228230
project.Services[i] = service
@@ -292,8 +294,6 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
292294
}
293295

294296
func (s *composeService) toBuildOptions(project *types.Project, service types.ServiceConfig, options api.BuildOptions) (build.Options, error) {
295-
tags := []string{service.Image}
296-
297297
buildArgs := flatten(service.Build.Args.Resolve(envResolver(project.Environment)))
298298

299299
for k, v := range storeutil.GetProxyConfig(s.dockerCli) {
@@ -335,6 +335,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
335335
sessionConfig = append(sessionConfig, secretsProvider)
336336
}
337337

338+
tags := []string{api.GetImageNameOrDefault(service, project.Name)}
338339
if len(service.Build.Tags) > 0 {
339340
tags = append(tags, service.Build.Tags...)
340341
}
@@ -345,18 +346,19 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
345346

346347
imageLabels := getImageBuildLabels(project, service)
347348

349+
push := options.Push && service.Image != ""
348350
exports := []bclient.ExportEntry{{
349351
Type: "docker",
350352
Attrs: map[string]string{
351353
"load": "true",
352-
"push": fmt.Sprint(options.Push),
354+
"push": fmt.Sprint(push),
353355
},
354356
}}
355357
if len(service.Build.Platforms) > 1 {
356358
exports = []bclient.ExportEntry{{
357359
Type: "image",
358360
Attrs: map[string]string{
359-
"push": fmt.Sprint(options.Push),
361+
"push": fmt.Sprint(push),
360362
},
361363
}}
362364
}

pkg/compose/build_classic.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import (
4545
)
4646

4747
//nolint:gocyclo
48-
func (s *composeService) doBuildClassic(ctx context.Context, service types.ServiceConfig, options api.BuildOptions) (string, error) {
48+
func (s *composeService) doBuildClassic(ctx context.Context, projectName string, service types.ServiceConfig, options api.BuildOptions) (string, error) {
4949
var (
5050
buildCtx io.ReadCloser
5151
dockerfileCtx io.ReadCloser
@@ -160,7 +160,8 @@ func (s *composeService) doBuildClassic(ctx context.Context, service types.Servi
160160
authConfigs[k] = registry.AuthConfig(auth)
161161
}
162162
buildOptions := imageBuildOptions(service.Build)
163-
buildOptions.Tags = append(buildOptions.Tags, service.Image)
163+
imageName := api.GetImageNameOrDefault(service, projectName)
164+
buildOptions.Tags = append(buildOptions.Tags, imageName)
164165
buildOptions.Dockerfile = relDockerfile
165166
buildOptions.AuthConfigs = authConfigs
166167
buildOptions.Memory = options.Memory

pkg/compose/pull.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,15 @@ func isServiceImageToBuild(service types.ServiceConfig, services []types.Service
313313
return true
314314
}
315315

316-
for _, depService := range services {
317-
if depService.Image == service.Image && depService.Build != nil {
316+
if service.Image == "" {
317+
// N.B. this should be impossible as service must have either `build` or `image` (or both)
318+
return false
319+
}
320+
321+
// look through the other services to see if another has a build definition for the same
322+
// image name
323+
for _, svc := range services {
324+
if svc.Image == service.Image && svc.Build != nil {
318325
return true
319326
}
320327
}

pkg/compose/viz.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func (s *composeService) Viz(_ context.Context, project *types.Project, opts api
5252
// dot is the perfect layout for this use case since graph is directed and hierarchical
5353
graphBuilder.WriteString(opts.Indentation + "layout=dot;\n")
5454

55-
addNodes(&graphBuilder, graph, &opts)
55+
addNodes(&graphBuilder, graph, project.Name, &opts)
5656
graphBuilder.WriteByte('\n')
5757

5858
addEdges(&graphBuilder, graph, &opts)
@@ -63,7 +63,7 @@ func (s *composeService) Viz(_ context.Context, project *types.Project, opts api
6363

6464
// addNodes adds the corresponding graphviz representation of all the nodes in the given graph to the graphBuilder
6565
// returns the same graphBuilder
66-
func addNodes(graphBuilder *strings.Builder, graph vizGraph, opts *api.VizOptions) *strings.Builder {
66+
func addNodes(graphBuilder *strings.Builder, graph vizGraph, projectName string, opts *api.VizOptions) *strings.Builder {
6767
for serviceNode := range graph {
6868
// write:
6969
// "service name" [style="filled" label<<font point-size="15">service name</font>
@@ -107,7 +107,7 @@ func addNodes(graphBuilder *strings.Builder, graph vizGraph, opts *api.VizOption
107107
if opts.IncludeImageName {
108108
graphBuilder.WriteString("<font point-size=\"10\">")
109109
graphBuilder.WriteString("<br/><br/><b>Image:</b><br/>")
110-
graphBuilder.WriteString(serviceNode.Image)
110+
graphBuilder.WriteString(api.GetImageNameOrDefault(*serviceNode, projectName))
111111
graphBuilder.WriteString("</font>")
112112
}
113113

pkg/e2e/build_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func TestLocalComposeBuild(t *testing.T) {
111111
t.Run(env+" no rebuild when up again", func(t *testing.T) {
112112
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "up", "-d")
113113

114-
assert.Assert(t, !strings.Contains(res.Stdout(), "COPY static"), res.Stdout())
114+
assert.Assert(t, !strings.Contains(res.Stdout(), "COPY static"))
115115
})
116116

117117
t.Run(env+" rebuild when up --build", func(t *testing.T) {
@@ -121,6 +121,11 @@ func TestLocalComposeBuild(t *testing.T) {
121121
res.Assert(t, icmd.Expected{Out: "COPY static2 /usr/share/nginx/html"})
122122
})
123123

124+
t.Run(env+" build --push ignored for unnamed images", func(t *testing.T) {
125+
res := c.RunDockerComposeCmd(t, "--workdir", "fixtures/build-test", "build", "--push", "nginx")
126+
assert.Assert(t, !strings.Contains(res.Stdout(), "failed to push"), res.Stdout())
127+
})
128+
124129
t.Run(env+" cleanup build project", func(t *testing.T) {
125130
c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "down")
126131
c.RunDockerCmd(t, "rmi", "build-test-nginx")

0 commit comments

Comments
 (0)