Browse Source

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 <[email protected]>
Guillaume Lours 2 years ago
parent
commit
eca1365d42
3 changed files with 66 additions and 29 deletions
  1. 14 1
      pkg/api/dryrunclient.go
  2. 0 11
      pkg/compose/build.go
  3. 52 17
      pkg/compose/build_buildkit.go

+ 14 - 1
pkg/api/dryrunclient.go

@@ -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) {
-	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) {

+ 0 - 11
pkg/compose/build.go

@@ -53,7 +53,6 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
 	}, s.stderr(), "Building")
 }
 
-//nolint:gocyclo
 func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions) (map[string]string, error) {
 	args := options.Args.Resolve(envResolver(project.Environment))
 
@@ -75,16 +74,6 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
 				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 service.Build.Args == nil {
 					service.Build.Args = args

+ 52 - 17
pkg/compose/build_buildkit.go

@@ -18,6 +18,8 @@ package compose
 
 import (
 	"context"
+	"crypto/sha1"
+	"fmt"
 	"os"
 	"path/filepath"
 
@@ -25,11 +27,13 @@ import (
 	_ "github.com/docker/buildx/driver/docker-container" //nolint:blank-imports
 	_ "github.com/docker/buildx/driver/kubernetes"       //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/builder"
 	"github.com/docker/buildx/util/dockerutil"
 	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) {
@@ -43,23 +47,27 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
 		return nil, err
 	}
 
-	// Progress needs its own context that lives longer than the
-	// build one otherwise it won't read all the messages from
-	// build and will lock
-	progressCtx, cancel := context.WithCancel(context.Background())
-	defer cancel()
-	w, err := xprogress.NewPrinter(progressCtx, s.stdout(), os.Stdout, mode)
-	if err != nil {
-		return nil, err
-	}
-
-	response, err := build.Build(ctx, nodes, opts, dockerutil.NewClient(s.dockerCli), filepath.Dir(s.configFile().Filename), w)
-	errW := w.Wait()
-	if err == nil {
-		err = errW
-	}
-	if err != nil {
-		return nil, WrapCategorisedComposeError(err, BuildFailure)
+	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
+		// build one otherwise it won't read all the messages from
+		// build and will lock
+		progressCtx, cancel := context.WithCancel(context.Background())
+		defer cancel()
+		w, err := xprogress.NewPrinter(progressCtx, s.stdout(), os.Stdout, mode)
+		if err != nil {
+			return nil, err
+		}
+		response, err = build.Build(ctx, nodes, opts, dockerutil.NewClient(s.dockerCli), filepath.Dir(s.configFile().Filename), w)
+		errW := w.Wait()
+		if err == nil {
+			err = errW
+		}
+		if err != nil {
+			return nil, WrapCategorisedComposeError(err, BuildFailure)
+		}
 	}
 
 	imagesBuilt := map[string]string{}
@@ -76,3 +84,30 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, opts map[string]bu
 
 	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
+}