diff --git a/pkg/cmd/builder/build.go b/pkg/cmd/builder/build.go index 9d1f0f7b05f..088420533f7 100644 --- a/pkg/cmd/builder/build.go +++ b/pkg/cmd/builder/build.go @@ -61,18 +61,22 @@ func (p platformParser) DefaultSpec() platforms.Platform { } func Build(ctx context.Context, client *containerd.Client, options types.BuilderBuildOptions) error { - buildctlBinary, buildctlArgs, needsLoading, metaFile, tags, cleanup, err := generateBuildctlArgs(ctx, client, options) + buildCtlArgs, err := generateBuildctlArgs(ctx, client, options) if err != nil { return err } - if cleanup != nil { - defer cleanup() + if buildCtlArgs.Cleanup != nil { + defer buildCtlArgs.Cleanup() } + buildctlBinary := buildCtlArgs.BuildctlBinary + buildctlArgs := buildCtlArgs.BuildctlArgs + log.L.Debugf("running %s %v", buildctlBinary, buildctlArgs) buildctlCmd := exec.Command(buildctlBinary, buildctlArgs...) buildctlCmd.Env = os.Environ() + needsLoading := buildCtlArgs.NeedsLoading var buildctlStdout io.Reader if needsLoading { buildctlStdout, err = buildctlCmd.StdoutPipe() @@ -95,7 +99,21 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder if err != nil { return err } - if err = loadImage(ctx, buildctlStdout, options.GOptions.Namespace, options.GOptions.Address, options.GOptions.Snapshotter, options.Stdout, platMC, options.Quiet); err != nil { + + // Write buildctl output to stdout + if _, err := io.Copy(os.Stdout, buildctlStdout); err != nil { + return err + } + + // Open the tar file + reader, err := os.Open(buildCtlArgs.DestFile) + if err != nil { + return fmt.Errorf("Failed to open file destination file: %v", err) + } + defer reader.Close() + + // Load the image into the containerd image store + if err = loadImage(ctx, reader, options.GOptions.Namespace, options.GOptions.Address, options.GOptions.Snapshotter, options.Stdout, platMC, options.Quiet); err != nil { return err } } @@ -105,7 +123,7 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder } if options.IidFile != "" { - id, err := getDigestFromMetaFile(metaFile) + id, err := getDigestFromMetaFile(buildCtlArgs.MetaFile) if err != nil { return err } @@ -114,6 +132,7 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder } } + tags := buildCtlArgs.Tags if len(tags) > 1 { log.L.Debug("Found more than 1 tag") imageService := client.ImageService() @@ -160,10 +179,15 @@ func loadImage(ctx context.Context, in io.Reader, namespace, address, snapshotte client.Close() }() r := &readCounter{Reader: in} - imgs, err := client.Import(ctx, r, containerd.WithDigestRef(archive.DigestTranslator(snapshotter)), containerd.WithSkipDigestRef(func(name string) bool { return name != "" }), containerd.WithImportPlatform(platMC)) + imgs, err := client.Import(ctx, r, + containerd.WithDigestRef(archive.DigestTranslator(snapshotter)), + containerd.WithSkipDigestRef(func(name string) bool { return name != "" }), + containerd.WithImportPlatform(platMC), + ) if err != nil { if r.N == 0 { // Avoid confusing "unrecognized image format" + log.L.Debugf("no image was built. error: %v", err) return errors.New("no image was built") } if errors.Is(err, images.ErrEmptyWalk) { @@ -192,23 +216,39 @@ func loadImage(ctx context.Context, in io.Reader, namespace, address, snapshotte return nil } -func generateBuildctlArgs(ctx context.Context, client *containerd.Client, options types.BuilderBuildOptions) (buildCtlBinary string, - buildctlArgs []string, needsLoading bool, metaFile string, tags []string, cleanup func(), err error) { +type BuildctlArgsResult struct { + BuildctlBinary string + BuildctlArgs []string + NeedsLoading bool + MetaFile string + DestFile string + Tags []string + Cleanup func() +} +func generateBuildctlArgs(ctx context.Context, client *containerd.Client, options types.BuilderBuildOptions) (result BuildctlArgsResult, err error) { buildctlBinary, err := buildkitutil.BuildctlBinary() if err != nil { - return "", nil, false, "", nil, nil, err + return result, err } + result.BuildctlBinary = buildctlBinary + + // Set the default destination file + var defaultDestFile = "output.tar" + // defaultDestFile, err := filepath.Abs("output.tar") + // if err != nil { + // return result, fmt.Errorf("failed to set the default destination file path: %v", err) + // } output := options.Output if output == "" { info, err := client.Server(ctx) if err != nil { - return "", nil, false, "", nil, nil, err + return result, err } sharable, err := isImageSharable(options.BuildKitHost, options.GOptions.Namespace, info.UUID, options.GOptions.Snapshotter, options.Platform) if err != nil { - return "", nil, false, "", nil, nil, err + return result, err } if sharable { output = "type=image,unpack=true" // ensure the target stage is unlazied (needed for any snapshotters) @@ -219,7 +259,9 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option // TODO: consider using type=oci for single-options.Platform build too output = "type=oci" } - needsLoading = true + + // output of the `buildctl` command needs to be loaded into the containerd image store. + result.NeedsLoading = true } } else { if !strings.Contains(output, "type=") { @@ -227,17 +269,14 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option // type=local,dest=