Skip to content

Commit

Permalink
WIP: allow individual manifests to opt-in to buildx
Browse files Browse the repository at this point in the history
Signed-off-by: Justin Chadwell <me@jedevc.com>
  • Loading branch information
jedevc committed May 17, 2022
1 parent 0dbb69e commit 7adace8
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 15 deletions.
39 changes: 29 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,22 @@ 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 {
// buildkit registry cache pulls from tagged images
caches := r.Tags(namespace, uniq, entry)
err = dockerBuildx(cacheTag, entry.ArchFile(arch), archive, ociArch.String(), caches)
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
56 changes: 51 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,14 +237,17 @@ 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...)
if platform != "" {
// https://github.com/docker/cli/blob/v20.10.7/cli/command/image/build.go#L163
cmd.Env = append(os.Environ(), "DOCKER_DEFAULT_PLATFORM="+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+

if debugFlag {
fmt.Printf("$ export %q\n", extraEnv)
fmt.Printf("$ export DOCKER_DEFAULT_PLATFORM=%q\n", platform)
}
}
cmd.Stdin = context
Expand All @@ -265,6 +268,49 @@ func dockerBuild(tag string, file string, context io.Reader, extraEnv []string)
}
}

const DockerfileSyntax = "docker/dockerfile:latest"

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

args = append(args, "--cache-to", "type=inline")
for _, cache := range caches {
args = append(args, "--cache-from", "type=registry,ref="+cache)
}

if platform != "" {
args = append(args, "--platform", platform)
}

// TODO: use makeGitContext when docker builder supports subdirectories
args = append(args, "-")

cmd := exec.Command("docker", args...)
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
}
}

func dockerTag(tag1 string, tag2 string) error {
if debugFlag {
fmt.Printf("$ docker tag %q %q\n", tag1, tag2)
Expand Down
10 changes: 10 additions & 0 deletions cmd/bashbrew/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,16 @@ func gitNormalizeForTagUsage(text string) string {

var gitRepoCache = map[string]string{}

func (r Repo) makeGitContext(arch string, entry *manifest.Manifest2822Entry) string {
context := fmt.Sprintf(
"%s#%s:%s",
entry.ArchGitRepo(arch),
entry.ArchGitCommit(arch),
entry.ArchDirectory(arch),
)
return context
}

func (r Repo) fetchGitRepo(arch string, entry *manifest.Manifest2822Entry) (string, error) {
cacheKey := strings.Join([]string{
entry.ArchGitRepo(arch),
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 7adace8

Please sign in to comment.