From b9b4dacdc0873f11c4daa16355d96d0038f45b24 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Mon, 2 Aug 2021 14:17:30 -0700 Subject: [PATCH] Set DOCKER_DEFAULT_PLATFORM when building "FROM scratch" images This will make sure "variant" gets set properly on the resulting images if our Docker version is 20.10+. We set this as tightly/sparingly as possible because the `--platform` flag is used for several different things inside Docker, the most visible being platform mismatch warnings/errors that we want to avoid. --- architecture/oci-platform.go | 11 +++++++++++ cmd/bashbrew/cmd-build.go | 11 ++++++++++- cmd/bashbrew/docker.go | 10 ++++++++-- cmd/bashbrew/main.go | 7 +++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/architecture/oci-platform.go b/architecture/oci-platform.go index 17c7969..727a902 100644 --- a/architecture/oci-platform.go +++ b/architecture/oci-platform.go @@ -1,5 +1,7 @@ package architecture +import "path" + // https://github.com/opencontainers/image-spec/blob/v1.0.1/image-index.md#image-index-property-descriptions // see "platform" (under "manifests") type OCIPlatform struct { @@ -25,3 +27,12 @@ var SupportedArches = map[string]OCIPlatform{ "windows-amd64": {OS: "windows", Architecture: "amd64"}, } + +// https://pkg.go.dev/github.com/containerd/containerd/platforms +func (p OCIPlatform) String() string { + return path.Join( + p.OS, + p.Architecture, + p.Variant, + ) +} diff --git a/cmd/bashbrew/cmd-build.go b/cmd/bashbrew/cmd-build.go index 06f357f..517839a 100644 --- a/cmd/bashbrew/cmd-build.go +++ b/cmd/bashbrew/cmd-build.go @@ -48,7 +48,9 @@ func cmdBuild(c *cli.Context) error { return cli.NewMultiError(fmt.Errorf(`failed fetching/scraping FROM for %q (tags %q)`, r.RepoName, entry.TagsString()), err) } + fromScratch := false for _, from := range froms { + fromScratch = fromScratch || from == "scratch" if from != "scratch" && pull != "never" { doPull := false switch pull { @@ -93,7 +95,14 @@ 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) - err = dockerBuild(cacheTag, entry.ArchFile(arch), archive) + 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) } diff --git a/cmd/bashbrew/docker.go b/cmd/bashbrew/docker.go index 203930a..3024d29 100644 --- a/cmd/bashbrew/docker.go +++ b/cmd/bashbrew/docker.go @@ -237,10 +237,16 @@ func (r Repo) dockerBuildUniqueBits(entry *manifest.Manifest2822Entry) ([]string return uniqueBits, nil } -func dockerBuild(tag string, file string, context io.Reader) error { - args := []string{"build", "-t", tag, "-f", file, "--rm", "--force-rm"} +func dockerBuild(tag string, file string, context io.Reader, extraEnv []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 debugFlag { + fmt.Printf("$ export %q\n", extraEnv) + } + } cmd.Stdin = context if debugFlag { cmd.Stdout = os.Stdout diff --git a/cmd/bashbrew/main.go b/cmd/bashbrew/main.go index 4f4a80e..2254821 100644 --- a/cmd/bashbrew/main.go +++ b/cmd/bashbrew/main.go @@ -8,6 +8,7 @@ import ( "github.com/urfave/cli" + "github.com/docker-library/bashbrew/architecture" "github.com/docker-library/bashbrew/manifest" ) @@ -22,6 +23,7 @@ var ( defaultCache string arch string + ociArch architecture.OCIPlatform namespace string constraints []string exclusiveConstraints bool @@ -166,6 +168,11 @@ func main() { constraints = c.GlobalStringSlice("constraint") exclusiveConstraints = c.GlobalBool("exclusive-constraints") + var ok bool + if ociArch, ok = architecture.SupportedArches[arch]; !ok { + return fmt.Errorf("invalid architecture: %q", arch) + } + archNamespaces = map[string]string{} for _, archMapping := range c.GlobalStringSlice("arch-namespace") { splitArchMapping := strings.SplitN(archMapping, "=", 2)