Skip to content

Commit

Permalink
[Windows] nerdctl build - Replace default type=docker with type=image
Browse files Browse the repository at this point in the history
    - Use default image name only when user has not specified one

Signed-off-by: Christine Murimi <[email protected]>
  • Loading branch information
TinaMor committed Nov 13, 2024
1 parent 8b814ca commit f4728b4
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 36 deletions.
2 changes: 1 addition & 1 deletion cmd/nerdctl/builder/builder_build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ CMD ["echo", "nerdctl-build-test-string"]`, testutil.CommonImage)
helpers.Ensure("build", data.Get("buildCtx"), "-t", data.Identifier(), "--output=type=docker,name="+data.Identifier("ignored"))
},
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
return helpers.Command("run", "--rm", data.Identifier())
return helpers.Command("run", "--rm", data.Identifier("ignored"))
},
Cleanup: func(data test.Data, helpers test.Helpers) {
helpers.Anyhow("rmi", "-f", data.Identifier())
Expand Down
5 changes: 4 additions & 1 deletion docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,10 @@ Flags:
- :whale: `--target`: Set the target build stage to build
- :whale: `--build-arg`: Set build-time variables
- :whale: `--no-cache`: Do not use cache when building the image
- :whale: `--output=OUTPUT`: Output destination (format: type=local,dest=path)
- :whale: `--output=OUTPUT`: Output destination (format: type=local,dest=path). See [buildctl output](https://github.com/moby/buildkit?tab=readme-ov-file#output) option for more details.

If this flag is not specified, it defaults to `type=image,name=docker.io/library/<TAG>:latest`.

- :whale: `type=local,dest=path/to/output-dir`: Local directory
- :whale: `type=oci[,dest=path/to/output.tar]`: Docker/OCI dual-format tar ball (compatible with `docker buildx build`)
- :whale: `type=docker[,dest=path/to/output.tar]`: Docker format tar ball (compatible with `docker buildx build`)
Expand Down
97 changes: 63 additions & 34 deletions pkg/cmd/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -95,6 +99,8 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder
if err != nil {
return err
}

// Load the image into the containerd image store
if err = loadImage(ctx, buildctlStdout, options.GOptions.Namespace, options.GOptions.Address, options.GOptions.Snapshotter, options.Stdout, platMC, options.Quiet); err != nil {
return err
}
Expand All @@ -105,7 +111,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
}
Expand All @@ -114,6 +120,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()
Expand Down Expand Up @@ -160,11 +167,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"
return errors.New("no image was built")
return fmt.Errorf("no image was built: %w", err)
}
if errors.Is(err, images.ErrEmptyWalk) {
err = fmt.Errorf("%w (Hint: set `--platform=PLATFORM` or `--all-platforms`)", err)
Expand Down Expand Up @@ -192,69 +203,85 @@ 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 {
BuildctlArgs []string
BuildctlBinary string
Cleanup func()
DestFile string
MetaFile string
NeedsLoading bool // Specifies whether the image needs to be loaded into the containerd image store
Tags []string
}

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

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)
} else {
output = "type=docker"
// https://github.com/moby/buildkit?tab=readme-ov-file#output
// type=image is the native type for containerd
output = "type=image"
if len(options.Platform) > 1 {
// For avoiding `error: failed to solve: docker exporter does not currently support exporting manifest lists`
// TODO: consider using type=oci for single-options.Platform build too
output = "type=oci"
}
needsLoading = true
}
} else {
if !strings.Contains(output, "type=") {
// should accept --output <DIR> as an alias of --output
// type=local,dest=<DIR>
output = fmt.Sprintf("type=local,dest=%s", output)
}
if strings.Contains(output, "type=docker") || strings.Contains(output, "type=oci") {
if !strings.Contains(output, "dest=") {
needsLoading = true
}
}

if strings.Contains(output, "type=docker") || strings.Contains(output, "type=oci") {
if !strings.Contains(output, "dest=") {
result.NeedsLoading = true
}
}

var tags []string
if tags = strutil.DedupeStrSlice(options.Tag); len(tags) > 0 {
ref := tags[0]
parsedReference, err := referenceutil.Parse(ref)
if err != nil {
return "", nil, false, "", nil, nil, err
return result, err
}
// Update the output with the the image name if it is not already set
if !strings.Contains(output, "name=") {
output += ",name=" + parsedReference.String()
}
output += ",name=" + parsedReference.String()

// pick the first tag and add it to output
for idx, tag := range tags {
parsedReference, err = referenceutil.Parse(tag)
if err != nil {
return "", nil, false, "", nil, nil, err
return result, err
}
tags[idx] = parsedReference.String()
}
} else if len(tags) == 0 {
output = output + ",dangling-name-prefix=<none>"
}
result.Tags = tags

buildctlArgs = buildkitutil.BuildctlBaseArgs(options.BuildKitHost)

buildctlArgs := buildkitutil.BuildctlBaseArgs(options.BuildKitHost)
buildctlArgs = append(buildctlArgs, []string{
"build",
"--progress=" + options.Progress,
Expand All @@ -271,9 +298,9 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
var err error
dir, err = buildkitutil.WriteTempDockerfile(options.Stdin)
if err != nil {
return "", nil, false, "", nil, nil, err
return result, err
}
cleanup = func() {
result.Cleanup = func() {
os.RemoveAll(dir)
}
} else {
Expand All @@ -286,12 +313,12 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
}
dir, file, err = buildkitutil.BuildKitFile(dir, file)
if err != nil {
return "", nil, false, "", nil, nil, err
return result, err
}

buildCtx, err := parseContextNames(options.ExtendedBuildContext)
if err != nil {
return "", nil, false, "", nil, nil, err
return result, err
}

for k, v := range buildCtx {
Expand All @@ -306,7 +333,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
if isOCILayout := strings.HasPrefix(v, "oci-layout://"); isOCILayout {
args, err := parseBuildContextFromOCILayout(k, v)
if err != nil {
return "", nil, false, "", nil, nil, err
return result, err
}

buildctlArgs = append(buildctlArgs, args...)
Expand All @@ -315,7 +342,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option

path, err := filepath.Abs(v)
if err != nil {
return "", nil, false, "", nil, nil, err
return result, err
}
buildctlArgs = append(buildctlArgs, fmt.Sprintf("--local=%s=%s", k, path))
buildctlArgs = append(buildctlArgs, fmt.Sprintf("--opt=context:%s=local:%s", k, k))
Expand Down Expand Up @@ -362,7 +389,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
}
}
} else {
return "", nil, false, "", nil, nil, fmt.Errorf("invalid build arg %q", ba)
return result, fmt.Errorf("invalid build arg %q", ba)
}
}

Expand Down Expand Up @@ -405,7 +432,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
optAttestType := strings.TrimPrefix(optAttestType, "type=")
buildctlArgs = append(buildctlArgs, fmt.Sprintf("--opt=attest:%s=%s", optAttestType, optAttestAttrs))
} else {
return "", nil, false, "", nil, nil, fmt.Errorf("attestation type not specified")
return result, fmt.Errorf("attestation type not specified")
}
}

Expand Down Expand Up @@ -434,11 +461,11 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
if options.IidFile != "" {
file, err := os.CreateTemp("", "buildkit-meta-*")
if err != nil {
return "", nil, false, "", nil, cleanup, err
return result, err
}
defer file.Close()
metaFile = file.Name()
buildctlArgs = append(buildctlArgs, "--metadata-file="+metaFile)
result.MetaFile = file.Name()
buildctlArgs = append(buildctlArgs, "--metadata-file="+result.MetaFile)
}

if options.NetworkMode != "" {
Expand All @@ -453,7 +480,9 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
}
}

return buildctlBinary, buildctlArgs, needsLoading, metaFile, tags, cleanup, nil
result.BuildctlArgs = buildctlArgs

return result, nil
}

func getDigestFromMetaFile(path string) (string, error) {
Expand Down

0 comments on commit f4728b4

Please sign in to comment.