cli: dry run support for `build` (#10502)

* add dry-run support for classic builder
* add dry-run support for buildkit

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
This commit is contained in:
Guillaume Lours 2023-05-02 20:23:26 +02:00 committed by GitHub
parent 03f4c0e631
commit eca1365d42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 29 deletions

View File

@ -165,7 +165,20 @@ func (d *DryRunClient) CopyToContainer(ctx context.Context, container, path stri
} }
func (d *DryRunClient) ImageBuild(ctx context.Context, reader io.Reader, options moby.ImageBuildOptions) (moby.ImageBuildResponse, error) { func (d *DryRunClient) ImageBuild(ctx context.Context, reader io.Reader, options moby.ImageBuildOptions) (moby.ImageBuildResponse, error) {
return moby.ImageBuildResponse{}, ErrNotImplemented jsonMessage, err := json.Marshal(&jsonmessage.JSONMessage{
Status: fmt.Sprintf("%[1]sSuccessfully built: dryRunID\n%[1]sSuccessfully tagged: %[2]s\n", DRYRUN_PREFIX, options.Tags[0]),
Progress: &jsonmessage.JSONProgress{},
ID: "",
})
if err != nil {
return moby.ImageBuildResponse{}, err
}
rc := io.NopCloser(bytes.NewReader(jsonMessage))
return moby.ImageBuildResponse{
Body: rc,
OSType: "",
}, nil
} }
func (d *DryRunClient) ImageInspectWithRaw(ctx context.Context, imageName string) (moby.ImageInspect, []byte, error) { func (d *DryRunClient) ImageInspectWithRaw(ctx context.Context, imageName string) (moby.ImageInspect, []byte, error) {

View File

@ -53,7 +53,6 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
}, s.stderr(), "Building") }, s.stderr(), "Building")
} }
//nolint:gocyclo
func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions) (map[string]string, error) { func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions) (map[string]string, error) {
args := options.Args.Resolve(envResolver(project.Environment)) args := options.Args.Resolve(envResolver(project.Environment))
@ -75,16 +74,6 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
return nil return nil
} }
//TODO:glours - condition to be removed when dry-run support of build will be implemented.
if s.dryRun {
builder := "buildkit"
if !buildkitEnabled {
builder = "legacy builder"
}
fmt.Printf("%sBuilding image %s with %s\n", api.DRYRUN_PREFIX, service.Image, builder)
return nil
}
if !buildkitEnabled { if !buildkitEnabled {
if service.Build.Args == nil { if service.Build.Args == nil {
service.Build.Args = args service.Build.Args = args

View File

@ -18,6 +18,8 @@ package compose
import ( import (
"context" "context"
"crypto/sha1"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -25,11 +27,13 @@ import (
_ "github.com/docker/buildx/driver/docker-container" //nolint:blank-imports _ "github.com/docker/buildx/driver/docker-container" //nolint:blank-imports
_ "github.com/docker/buildx/driver/kubernetes" //nolint:blank-imports _ "github.com/docker/buildx/driver/kubernetes" //nolint:blank-imports
_ "github.com/docker/buildx/driver/remote" //nolint:blank-imports _ "github.com/docker/buildx/driver/remote" //nolint:blank-imports
"github.com/moby/buildkit/client"
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
"github.com/docker/buildx/builder" "github.com/docker/buildx/builder"
"github.com/docker/buildx/util/dockerutil" "github.com/docker/buildx/util/dockerutil"
xprogress "github.com/docker/buildx/util/progress" xprogress "github.com/docker/buildx/util/progress"
"github.com/docker/compose/v2/pkg/progress"
) )
func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]build.Options, mode string) (map[string]string, error) { func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]build.Options, mode string) (map[string]string, error) {
@ -43,6 +47,10 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
return nil, err return nil, err
} }
var response map[string]*client.SolveResponse
if s.dryRun {
response = s.dryRunBuildResponse(ctx, opts)
} else {
// Progress needs its own context that lives longer than the // Progress needs its own context that lives longer than the
// build one otherwise it won't read all the messages from // build one otherwise it won't read all the messages from
// build and will lock // build and will lock
@ -52,8 +60,7 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
if err != nil { if err != nil {
return nil, err return nil, err
} }
response, err = build.Build(ctx, nodes, opts, dockerutil.NewClient(s.dockerCli), filepath.Dir(s.configFile().Filename), w)
response, err := build.Build(ctx, nodes, opts, dockerutil.NewClient(s.dockerCli), filepath.Dir(s.configFile().Filename), w)
errW := w.Wait() errW := w.Wait()
if err == nil { if err == nil {
err = errW err = errW
@ -61,6 +68,7 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
if err != nil { if err != nil {
return nil, WrapCategorisedComposeError(err, BuildFailure) return nil, WrapCategorisedComposeError(err, BuildFailure)
} }
}
imagesBuilt := map[string]string{} imagesBuilt := map[string]string{}
for name, img := range response { for name, img := range response {
@ -76,3 +84,30 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
return imagesBuilt, err return imagesBuilt, err
} }
func (s composeService) dryRunBuildResponse(ctx context.Context, options map[string]build.Options) map[string]*client.SolveResponse {
w := progress.ContextWriter(ctx)
buildResponse := map[string]*client.SolveResponse{}
for name, option := range options {
dryRunUUID := fmt.Sprintf("dryRun-%x", sha1.Sum([]byte(name)))
w.Event(progress.Event{
ID: " ",
Status: progress.Done,
Text: fmt.Sprintf("build service %s", name),
})
w.Event(progress.Event{
ID: "==>",
Status: progress.Done,
Text: fmt.Sprintf("==> writing image %s", dryRunUUID),
})
w.Event(progress.Event{
ID: "==> ==>",
Status: progress.Done,
Text: fmt.Sprintf(`naming to %s`, option.Tags[0]),
})
buildResponse[name] = &client.SolveResponse{ExporterResponse: map[string]string{
"containerimage.digest": dryRunUUID,
}}
}
return buildResponse
}