Skip to content

Commit

Permalink
buildkit: introduce initial support
Browse files Browse the repository at this point in the history
This patch adds a new entry `Buildkit` to the RFC2822 image manifest
files, allowing individual image components to opt into buildkit
support, using docker buildx.

Additionally, docker build is changed to explicitly set
DOCKER_BUILDKIT=0 to force a non-buildkit build in preparation for
switching the default in any upcoming docker versions.

Signed-off-by: Justin Chadwell <me@jedevc.com>
  • Loading branch information
jedevc committed Jun 28, 2022
1 parent 92ab34b commit 1cb5cd9
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 15 deletions.
37 changes: 27 additions & 10 deletions cmd/bashbrew/cmd-build.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"strings"

"github.com/urfave/cli"
)
Expand Down Expand Up @@ -77,6 +78,18 @@ func cmdBuild(c *cli.Context) error {
return cli.NewMultiError(fmt.Errorf(`failed calculating "cache hash" for %q (tags %q)`, r.RepoName, entry.TagsString()), err)
}

var buildkit bool
switch strings.ToLower(entry.Buildkit) {
case "false":
buildkit = false
case "true":
buildkit = true
case "":
buildkit = false
default:
return cli.NewMultiError(fmt.Errorf(`invalid value for buildkit %s, expected "true" or "false"`, entry.Buildkit))
}

// check whether we've already built this artifact
_, err = dockerInspect("{{.Id}}", cacheTag)
if err != nil {
Expand All @@ -95,16 +108,20 @@ func cmdBuild(c *cli.Context) error {

// TODO use "meta.StageNames" to do "docker build --target" so we can tag intermediate stages too for cache (streaming "git archive" directly to "docker build" makes that a little hard to accomplish without re-streaming)

var extraEnv []string = nil
if fromScratch {
// https://github.com/docker/cli/blob/v20.10.7/cli/command/image/build.go#L163
extraEnv = []string{"DOCKER_DEFAULT_PLATFORM=" + ociArch.String()}
// ideally, we would set this via an explicit "--platform" flag on "docker build", but it's not supported without buildkit until 20.10+ and this is a trivial way to get Docker to do the right thing in both cases without explicitly trying to detect whether we're on 20.10+
}

err = dockerBuild(cacheTag, entry.ArchFile(arch), archive, extraEnv)
if err != nil {
return cli.NewMultiError(fmt.Errorf(`failed building %q (tags %q)`, r.RepoName, entry.TagsString()), err)
if buildkit {
err = dockerBuildx(cacheTag, entry.ArchFile(arch), archive, ociArch.String())
if err != nil {
return cli.NewMultiError(fmt.Errorf(`failed building %q (tags %q)`, r.RepoName, entry.TagsString()), err)
}
} else {
var platform string
if fromScratch {
platform = ociArch.String()
}
err = dockerBuild(cacheTag, entry.ArchFile(arch), archive, platform)
if err != nil {
return cli.NewMultiError(fmt.Errorf(`failed building %q (tags %q)`, r.RepoName, entry.TagsString()), err)
}
}
archive.Close() // be sure this happens sooner rather than later (defer might take a while, and we want to reap zombies more aggressively)
}
Expand Down
51 changes: 46 additions & 5 deletions cmd/bashbrew/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"strconv"
"strings"

"github.com/urfave/cli"
"github.com/docker-library/bashbrew/manifest"
"github.com/urfave/cli"
)

type dockerfileMetadata struct {
Expand Down Expand Up @@ -237,16 +237,57 @@ func (r Repo) dockerBuildUniqueBits(entry *manifest.Manifest2822Entry) ([]string
return uniqueBits, nil
}

func dockerBuild(tag string, file string, context io.Reader, extraEnv []string) error {
func dockerBuild(tag string, file string, context io.Reader, platform string) error {
args := []string{"build", "--tag", tag, "--file", file, "--rm", "--force-rm"}
args = append(args, "-")
cmd := exec.Command("docker", args...)
if extraEnv != nil {
cmd.Env = append(os.Environ(), extraEnv...)
cmd.Env = append(os.Environ(), "DOCKER_BUILDKIT=0")
if debugFlag {
fmt.Println("$ export DOCKER_BUILDKIT=0")
}
if platform != "" {
// ideally, we would set this via an explicit "--platform" flag on "docker build", but it's not supported without buildkit until 20.10+ and this is a trivial way to get Docker to do the right thing in both cases without explicitly trying to detect whether we're on 20.10+
// https://github.com/docker/cli/blob/v20.10.7/cli/command/image/build.go#L163
cmd.Env = append(cmd.Env, "DOCKER_DEFAULT_PLATFORM="+platform)
if debugFlag {
fmt.Printf("$ export %q\n", extraEnv)
fmt.Printf("$ export DOCKER_DEFAULT_PLATFORM=%q\n", platform)
}
}
cmd.Stdin = context
if debugFlag {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
fmt.Printf("$ docker %q\n", args)
return cmd.Run()
} else {
buf := &bytes.Buffer{}
cmd.Stdout = buf
cmd.Stderr = buf
err := cmd.Run()
if err != nil {
err = cli.NewMultiError(err, fmt.Errorf(`docker %q output:%s`, args, "\n"+buf.String()))
}
return err
}
}

const dockerfileSyntax = "docker/dockerfile:1"

func dockerBuildx(tag string, file string, context io.Reader, platform string) error {
args := []string{
"buildx",
"build",
"--progress", "plain",
"--build-arg", "BUILDKIT_SYNTAX=" + dockerfileSyntax,
"--tag", tag,
"--file", file,
}
if platform != "" {
args = append(args, "--platform", platform)
}
args = append(args, "-")

cmd := exec.Command("docker", args...)
cmd.Stdin = context
if debugFlag {
cmd.Stdout = os.Stdout
Expand Down
2 changes: 2 additions & 0 deletions manifest/rfc2822.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type Manifest2822Entry struct {
Directory string
File string

Buildkit string

// architecture-specific versions of the above fields
ArchValues map[string]string
// "ARCH-FIELD: VALUE"
Expand Down

0 comments on commit 1cb5cd9

Please sign in to comment.