Skip to content

Commit 67455e9

Browse files
authored
fix builkit progressui integration (docker#10535)
Signed-off-by: Nicolas De Loof <[email protected]>
1 parent 5fdcaa0 commit 67455e9

File tree

3 files changed

+89
-77
lines changed

3 files changed

+89
-77
lines changed

pkg/compose/build.go

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package compose
1919
import (
2020
"context"
2121
"fmt"
22+
"os"
2223
"path/filepath"
2324

2425
"github.com/compose-spec/compose-go/types"
@@ -53,68 +54,83 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
5354
}, s.stderr(), "Building")
5455
}
5556

56-
func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions) (map[string]string, error) {
57+
func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions) (map[string]string, error) { //nolint:gocyclo
5758
args := options.Args.Resolve(envResolver(project.Environment))
5859

5960
buildkitEnabled, err := s.dockerCli.BuildKitEnabled()
6061
if err != nil {
6162
return nil, err
6263
}
64+
65+
// Progress needs its own context that lives longer than the
66+
// build one otherwise it won't read all the messages from
67+
// build and will lock
68+
progressCtx, cancel := context.WithCancel(context.Background())
69+
defer cancel()
70+
w, err := xprogress.NewPrinter(progressCtx, s.stdout(), os.Stdout, options.Progress)
71+
if err != nil {
72+
return nil, err
73+
}
74+
6375
builtIDs := make([]string, len(project.Services))
6476
err = InDependencyOrder(ctx, project, func(ctx context.Context, name string) error {
6577
if len(options.Services) > 0 && !utils.Contains(options.Services, name) {
6678
return nil
6779
}
68-
for i, service := range project.Services {
69-
if service.Name != name {
70-
continue
71-
}
72-
73-
if service.Build == nil {
74-
return nil
75-
}
80+
service, idx := getServiceIndex(project, name)
7681

77-
if !buildkitEnabled {
78-
if service.Build.Args == nil {
79-
service.Build.Args = args
80-
} else {
81-
service.Build.Args = service.Build.Args.OverrideBy(args)
82-
}
83-
id, err := s.doBuildClassic(ctx, service, options)
84-
if err != nil {
85-
return err
86-
}
87-
builtIDs[i] = id
88-
89-
if options.Push {
90-
return s.push(ctx, project, api.PushOptions{})
91-
}
92-
return nil
93-
}
82+
if service.Build == nil {
83+
return nil
84+
}
9485

95-
if options.Memory != 0 {
96-
fmt.Fprintln(s.stderr(), "WARNING: --memory is not supported by BuildKit and will be ignored.")
86+
if !buildkitEnabled {
87+
if service.Build.Args == nil {
88+
service.Build.Args = args
89+
} else {
90+
service.Build.Args = service.Build.Args.OverrideBy(args)
9791
}
98-
99-
buildOptions, err := s.toBuildOptions(project, service, options)
92+
id, err := s.doBuildClassic(ctx, service, options)
10093
if err != nil {
10194
return err
10295
}
103-
buildOptions.BuildArgs = mergeArgs(buildOptions.BuildArgs, flatten(args))
104-
opts := map[string]build.Options{service.Name: buildOptions}
96+
builtIDs[idx] = id
10597

106-
ids, err := s.doBuildBuildkit(ctx, opts, options.Progress)
107-
if err != nil {
108-
return err
98+
if options.Push {
99+
return s.push(ctx, project, api.PushOptions{})
109100
}
110-
builtIDs[i] = ids[service.Name]
111101
return nil
112102
}
103+
104+
if options.Memory != 0 {
105+
fmt.Fprintln(s.stderr(), "WARNING: --memory is not supported by BuildKit and will be ignored.")
106+
}
107+
108+
buildOptions, err := s.toBuildOptions(project, service, options)
109+
if err != nil {
110+
return err
111+
}
112+
buildOptions.BuildArgs = mergeArgs(buildOptions.BuildArgs, flatten(args))
113+
114+
ids, err := s.doBuildBuildkit(ctx, service.Name, buildOptions, w)
115+
if err != nil {
116+
return err
117+
}
118+
builtIDs[idx] = ids[service.Name]
119+
113120
return nil
114121
}, func(traversal *graphTraversal) {
115122
traversal.maxConcurrency = s.maxConcurrency
116123
})
117124

125+
// enforce all build event get consumed
126+
if errw := w.Wait(); errw != nil {
127+
return nil, errw
128+
}
129+
130+
if err != nil {
131+
return nil, err
132+
}
133+
118134
imageIDs := map[string]string{}
119135
for i, d := range builtIDs {
120136
if d != "" {
@@ -124,6 +140,18 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
124140
return imageIDs, err
125141
}
126142

143+
func getServiceIndex(project *types.Project, name string) (types.ServiceConfig, int) {
144+
var service types.ServiceConfig
145+
var idx int
146+
for i, s := range project.Services {
147+
if s.Name == name {
148+
idx, service = i, s
149+
break
150+
}
151+
}
152+
return service, idx
153+
}
154+
127155
func (s *composeService) ensureImagesExists(ctx context.Context, project *types.Project, quietPull bool) error {
128156
for _, service := range project.Services {
129157
if service.Image == "" && service.Build == nil {

pkg/compose/build_buildkit.go

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,22 @@ import (
2020
"context"
2121
"crypto/sha1"
2222
"fmt"
23-
"os"
2423
"path/filepath"
2524

2625
_ "github.com/docker/buildx/driver/docker" //nolint:blank-imports
2726
_ "github.com/docker/buildx/driver/docker-container" //nolint:blank-imports
2827
_ "github.com/docker/buildx/driver/kubernetes" //nolint:blank-imports
2928
_ "github.com/docker/buildx/driver/remote" //nolint:blank-imports
29+
buildx "github.com/docker/buildx/util/progress"
3030
"github.com/moby/buildkit/client"
3131

3232
"github.com/docker/buildx/build"
3333
"github.com/docker/buildx/builder"
3434
"github.com/docker/buildx/util/dockerutil"
35-
xprogress "github.com/docker/buildx/util/progress"
3635
"github.com/docker/compose/v2/pkg/progress"
3736
)
3837

39-
func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]build.Options, mode string) (map[string]string, error) {
38+
func (s *composeService) doBuildBuildkit(ctx context.Context, service string, opts build.Options, p *buildx.Printer) (map[string]string, error) {
4039
b, err := builder.New(s.dockerCli)
4140
if err != nil {
4241
return nil, err
@@ -49,22 +48,9 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
4948

5049
var response map[string]*client.SolveResponse
5150
if s.dryRun {
52-
response = s.dryRunBuildResponse(ctx, opts)
51+
response = s.dryRunBuildResponse(ctx, service, opts)
5352
} else {
54-
// Progress needs its own context that lives longer than the
55-
// build one otherwise it won't read all the messages from
56-
// build and will lock
57-
progressCtx, cancel := context.WithCancel(context.Background())
58-
defer cancel()
59-
w, err := xprogress.NewPrinter(progressCtx, s.stdout(), os.Stdout, mode)
60-
if err != nil {
61-
return nil, err
62-
}
63-
response, err = build.Build(ctx, nodes, opts, dockerutil.NewClient(s.dockerCli), filepath.Dir(s.configFile().Filename), w)
64-
errW := w.Wait()
65-
if err == nil {
66-
err = errW
67-
}
53+
response, err = build.Build(ctx, nodes, map[string]build.Options{service: opts}, dockerutil.NewClient(s.dockerCli), filepath.Dir(s.configFile().Filename), buildx.WithPrefix(p, service, true))
6854
if err != nil {
6955
return nil, WrapCategorisedComposeError(err, BuildFailure)
7056
}
@@ -85,29 +71,27 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
8571
return imagesBuilt, err
8672
}
8773

88-
func (s composeService) dryRunBuildResponse(ctx context.Context, options map[string]build.Options) map[string]*client.SolveResponse {
74+
func (s composeService) dryRunBuildResponse(ctx context.Context, name string, options build.Options) map[string]*client.SolveResponse {
8975
w := progress.ContextWriter(ctx)
9076
buildResponse := map[string]*client.SolveResponse{}
91-
for name, option := range options {
92-
dryRunUUID := fmt.Sprintf("dryRun-%x", sha1.Sum([]byte(name)))
93-
w.Event(progress.Event{
94-
ID: " ",
95-
Status: progress.Done,
96-
Text: fmt.Sprintf("build service %s", name),
97-
})
98-
w.Event(progress.Event{
99-
ID: "==>",
100-
Status: progress.Done,
101-
Text: fmt.Sprintf("==> writing image %s", dryRunUUID),
102-
})
103-
w.Event(progress.Event{
104-
ID: "==> ==>",
105-
Status: progress.Done,
106-
Text: fmt.Sprintf(`naming to %s`, option.Tags[0]),
107-
})
108-
buildResponse[name] = &client.SolveResponse{ExporterResponse: map[string]string{
109-
"containerimage.digest": dryRunUUID,
110-
}}
111-
}
77+
dryRunUUID := fmt.Sprintf("dryRun-%x", sha1.Sum([]byte(name)))
78+
w.Event(progress.Event{
79+
ID: " ",
80+
Status: progress.Done,
81+
Text: fmt.Sprintf("build service %s", name),
82+
})
83+
w.Event(progress.Event{
84+
ID: "==>",
85+
Status: progress.Done,
86+
Text: fmt.Sprintf("==> writing image %s", dryRunUUID),
87+
})
88+
w.Event(progress.Event{
89+
ID: "==> ==>",
90+
Status: progress.Done,
91+
Text: fmt.Sprintf(`naming to %s`, options.Tags[0]),
92+
})
93+
buildResponse[name] = &client.SolveResponse{ExporterResponse: map[string]string{
94+
"containerimage.digest": dryRunUUID,
95+
}}
11296
return buildResponse
11397
}

pkg/e2e/cancel_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ func TestComposeCancel(t *testing.T) {
5959
}, 30*time.Second, 1*time.Second)
6060

6161
err = syscall.Kill(-cmd.Process.Pid, syscall.SIGINT) // simulate Ctrl-C : send signal to processGroup, children will have same groupId by default
62-
6362
assert.NilError(t, err)
63+
6464
c.WaitForCondition(t, func() (bool, string) {
6565
out := stdout.String()
6666
errors := stderr.String()

0 commit comments

Comments
 (0)