2022-02-03 04:37:55 -05:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-11-03 17:30:27 -04:00
|
|
|
"io"
|
2022-02-03 04:37:55 -05:00
|
|
|
"os"
|
|
|
|
"strconv"
|
2022-11-03 17:30:27 -04:00
|
|
|
"strings"
|
2022-02-03 04:37:55 -05:00
|
|
|
|
|
|
|
pluginmanager "github.com/docker/cli/cli-plugins/manager"
|
|
|
|
"github.com/docker/cli/cli/command"
|
build: allow BuildKit to be used on Windows daemons that advertise it
Commit 6fef143dbcf5fdcb436d05cf8e67d56c24bc4e6e switched the CLI to use
BuildKit by default, but as part of that removed the use of the
BuildkitVersion field as returned by Ping.
Some follow-up changes in commits e38e6c51ffb55405321038dfcf3a477f and
e7a8748b939fbf7798352cff1e0b87ef updated the logic for detecting whether
BuildKit should be used or the legacy builder, but hard-coded using the
legacy builder for Windows daemons.
While Windows / WCOW does not yet support BuildKit by default, there is
work in progress to implement it, so we should not hard-code the assumption
that a Windows daemon cannot support BuildKit.
On the daemon-side, [moby@7b153b9] (Docker v23.0) changed the default as
advertised by the daemon to be BuildKit for Linux daemons. That change
still hardcoded BuildKit to be unsupported for Windows daemons (and does
not yet allow overriding the config), but this may change for future
versions of the daemon, or test-builds.
This patch:
- Re-introduces checks for the BuildkitVersion field in the "Ping" response.
- If the Ping response from the daemon advertises that it supports BuildKit,
the CLI will now use BuildKit as builder.
- If we didn't get a Ping response, or the Ping response did NOT advertise
that the daemon supported BuildKit, we continue to use the current
defaults (BuildKit for Linux daemons, and the legacy builder for Windows)
- Handling of the DOCKER_BUILDKIT environment variable is unchanged; for
CLI.BuildKitEnabled, DOCKER_BUILDKIT always takes precedence, and for
processBuilder the value is taken into account, but will print a warning
when BuildKit is disabled and a Linux daemon is used. For Windows daemons,
no warning is printed.
[moby@7b153b9]: https://github.com/moby/moby/commit/7b153b9e28b53779215de209736310f9266b3f2f
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-19 18:46:30 -04:00
|
|
|
"github.com/docker/docker/api/types"
|
2022-02-03 04:37:55 -05:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/spf13/cobra"
|
2022-11-03 17:30:27 -04:00
|
|
|
"github.com/spf13/pflag"
|
2022-02-03 04:37:55 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2022-01-27 12:42:05 -05:00
|
|
|
builderDefaultPlugin = "buildx"
|
|
|
|
buildxMissingWarning = `DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
|
|
|
|
Install the buildx component to build images with BuildKit:
|
2022-12-06 07:46:51 -05:00
|
|
|
https://docs.docker.com/go/buildx/`
|
|
|
|
|
|
|
|
buildkitDisabledWarning = `DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
|
2022-12-19 07:03:28 -05:00
|
|
|
BuildKit is currently disabled; enable it by removing the DOCKER_BUILDKIT=0
|
2022-12-06 07:46:51 -05:00
|
|
|
environment-variable.`
|
2022-02-03 04:37:55 -05:00
|
|
|
|
2022-01-27 12:42:05 -05:00
|
|
|
buildxMissingError = `ERROR: BuildKit is enabled but the buildx component is missing or broken.
|
|
|
|
Install the buildx component to build images with BuildKit:
|
2022-12-06 07:46:51 -05:00
|
|
|
https://docs.docker.com/go/buildx/`
|
2022-01-27 12:42:05 -05:00
|
|
|
)
|
2022-02-03 04:37:55 -05:00
|
|
|
|
2022-12-06 07:46:51 -05:00
|
|
|
func newBuilderError(errorMsg string, pluginLoadErr error) error {
|
|
|
|
if pluginmanager.IsNotFound(pluginLoadErr) {
|
2022-01-27 12:42:05 -05:00
|
|
|
return errors.New(errorMsg)
|
2022-02-03 04:37:55 -05:00
|
|
|
}
|
2022-12-06 07:46:51 -05:00
|
|
|
if pluginLoadErr != nil {
|
|
|
|
return fmt.Errorf("%w\n\n%s", pluginLoadErr, errorMsg)
|
2022-02-03 13:11:05 -05:00
|
|
|
}
|
2022-12-06 07:46:51 -05:00
|
|
|
return errors.New(errorMsg)
|
2022-02-03 04:37:55 -05:00
|
|
|
}
|
|
|
|
|
2022-11-03 17:30:27 -04:00
|
|
|
//nolint:gocyclo
|
|
|
|
func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []string) ([]string, []string, []string, error) {
|
2022-12-06 07:46:51 -05:00
|
|
|
var buildKitDisabled, useBuilder, useAlias bool
|
2022-11-03 17:30:27 -04:00
|
|
|
var envs []string
|
2022-02-03 13:11:05 -05:00
|
|
|
|
2023-04-19 08:06:01 -04:00
|
|
|
// check DOCKER_BUILDKIT env var is not empty
|
|
|
|
// if it is assume we want to use the builder component
|
|
|
|
if v := os.Getenv("DOCKER_BUILDKIT"); v != "" {
|
2022-02-03 04:37:55 -05:00
|
|
|
enabled, err := strconv.ParseBool(v)
|
|
|
|
if err != nil {
|
2022-11-03 17:30:27 -04:00
|
|
|
return args, osargs, nil, errors.Wrap(err, "DOCKER_BUILDKIT environment variable expects boolean value")
|
2022-02-03 04:37:55 -05:00
|
|
|
}
|
|
|
|
if !enabled {
|
2022-12-06 07:46:51 -05:00
|
|
|
buildKitDisabled = true
|
2022-02-03 13:11:05 -05:00
|
|
|
} else {
|
|
|
|
useBuilder = true
|
2022-02-03 04:37:55 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if a builder alias is defined, use it instead
|
|
|
|
// of the default one
|
|
|
|
builderAlias := builderDefaultPlugin
|
|
|
|
aliasMap := dockerCli.ConfigFile().Aliases
|
|
|
|
if v, ok := aliasMap[keyBuilderAlias]; ok {
|
2022-02-03 13:11:05 -05:00
|
|
|
useBuilder = true
|
2022-11-03 18:35:45 -04:00
|
|
|
useAlias = true
|
2022-02-03 04:37:55 -05:00
|
|
|
builderAlias = v
|
|
|
|
}
|
|
|
|
|
2022-02-03 13:11:05 -05:00
|
|
|
// is this a build that should be forwarded to the builder?
|
2024-03-15 11:24:05 -04:00
|
|
|
fwargs, fwosargs, fwcmdpath, forwarded := forwardBuilder(builderAlias, args, osargs)
|
2022-02-03 13:11:05 -05:00
|
|
|
if !forwarded {
|
2022-11-03 17:30:27 -04:00
|
|
|
return args, osargs, nil, nil
|
2022-01-27 13:30:06 -05:00
|
|
|
}
|
|
|
|
|
build: allow BuildKit to be used on Windows daemons that advertise it
Commit 6fef143dbcf5fdcb436d05cf8e67d56c24bc4e6e switched the CLI to use
BuildKit by default, but as part of that removed the use of the
BuildkitVersion field as returned by Ping.
Some follow-up changes in commits e38e6c51ffb55405321038dfcf3a477f and
e7a8748b939fbf7798352cff1e0b87ef updated the logic for detecting whether
BuildKit should be used or the legacy builder, but hard-coded using the
legacy builder for Windows daemons.
While Windows / WCOW does not yet support BuildKit by default, there is
work in progress to implement it, so we should not hard-code the assumption
that a Windows daemon cannot support BuildKit.
On the daemon-side, [moby@7b153b9] (Docker v23.0) changed the default as
advertised by the daemon to be BuildKit for Linux daemons. That change
still hardcoded BuildKit to be unsupported for Windows daemons (and does
not yet allow overriding the config), but this may change for future
versions of the daemon, or test-builds.
This patch:
- Re-introduces checks for the BuildkitVersion field in the "Ping" response.
- If the Ping response from the daemon advertises that it supports BuildKit,
the CLI will now use BuildKit as builder.
- If we didn't get a Ping response, or the Ping response did NOT advertise
that the daemon supported BuildKit, we continue to use the current
defaults (BuildKit for Linux daemons, and the legacy builder for Windows)
- Handling of the DOCKER_BUILDKIT environment variable is unchanged; for
CLI.BuildKitEnabled, DOCKER_BUILDKIT always takes precedence, and for
processBuilder the value is taken into account, but will print a warning
when BuildKit is disabled and a Linux daemon is used. For Windows daemons,
no warning is printed.
[moby@7b153b9]: https://github.com/moby/moby/commit/7b153b9e28b53779215de209736310f9266b3f2f
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-19 18:46:30 -04:00
|
|
|
if !useBuilder {
|
|
|
|
// Builder is not explicitly configured as an alias for buildx.
|
|
|
|
// Detect whether we should use BuildKit, or fallback to the
|
|
|
|
// legacy builder.
|
|
|
|
if si := dockerCli.ServerInfo(); si.BuildkitVersion != types.BuilderBuildKit && si.OSType == "windows" {
|
|
|
|
// The daemon didn't advertise BuildKit as the preferred builder,
|
|
|
|
// so use the legacy builder, which is still the default for
|
|
|
|
// Windows / WCOW.
|
|
|
|
return args, osargs, nil, nil
|
|
|
|
}
|
2022-02-23 10:20:45 -05:00
|
|
|
}
|
|
|
|
|
2022-12-06 07:46:51 -05:00
|
|
|
if buildKitDisabled {
|
build: allow BuildKit to be used on Windows daemons that advertise it
Commit 6fef143dbcf5fdcb436d05cf8e67d56c24bc4e6e switched the CLI to use
BuildKit by default, but as part of that removed the use of the
BuildkitVersion field as returned by Ping.
Some follow-up changes in commits e38e6c51ffb55405321038dfcf3a477f and
e7a8748b939fbf7798352cff1e0b87ef updated the logic for detecting whether
BuildKit should be used or the legacy builder, but hard-coded using the
legacy builder for Windows daemons.
While Windows / WCOW does not yet support BuildKit by default, there is
work in progress to implement it, so we should not hard-code the assumption
that a Windows daemon cannot support BuildKit.
On the daemon-side, [moby@7b153b9] (Docker v23.0) changed the default as
advertised by the daemon to be BuildKit for Linux daemons. That change
still hardcoded BuildKit to be unsupported for Windows daemons (and does
not yet allow overriding the config), but this may change for future
versions of the daemon, or test-builds.
This patch:
- Re-introduces checks for the BuildkitVersion field in the "Ping" response.
- If the Ping response from the daemon advertises that it supports BuildKit,
the CLI will now use BuildKit as builder.
- If we didn't get a Ping response, or the Ping response did NOT advertise
that the daemon supported BuildKit, we continue to use the current
defaults (BuildKit for Linux daemons, and the legacy builder for Windows)
- Handling of the DOCKER_BUILDKIT environment variable is unchanged; for
CLI.BuildKitEnabled, DOCKER_BUILDKIT always takes precedence, and for
processBuilder the value is taken into account, but will print a warning
when BuildKit is disabled and a Linux daemon is used. For Windows daemons,
no warning is printed.
[moby@7b153b9]: https://github.com/moby/moby/commit/7b153b9e28b53779215de209736310f9266b3f2f
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-19 18:46:30 -04:00
|
|
|
// When using a Linux daemon, print a warning that the legacy builder
|
|
|
|
// is deprecated. For Windows / WCOW, BuildKit is still experimental,
|
|
|
|
// so we don't print this warning, even if the daemon advertised that
|
|
|
|
// it supports BuildKit.
|
2022-02-03 13:11:05 -05:00
|
|
|
if dockerCli.ServerInfo().OSType != "windows" {
|
2022-12-06 07:46:51 -05:00
|
|
|
_, _ = fmt.Fprintf(dockerCli.Err(), "%s\n\n", buildkitDisabledWarning)
|
2022-02-03 13:11:05 -05:00
|
|
|
}
|
2022-11-03 17:30:27 -04:00
|
|
|
return args, osargs, nil, nil
|
2022-01-27 13:30:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// check plugin is available if cmd forwarded
|
|
|
|
plugin, perr := pluginmanager.GetPlugin(builderAlias, dockerCli, cmd.Root())
|
|
|
|
if perr == nil && plugin != nil {
|
|
|
|
perr = plugin.Err
|
|
|
|
}
|
|
|
|
if perr != nil {
|
2022-12-06 07:46:51 -05:00
|
|
|
// if builder is enforced with DOCKER_BUILDKIT=1, cmd must fail
|
|
|
|
// if the plugin is missing or broken.
|
2022-02-03 13:11:05 -05:00
|
|
|
if useBuilder {
|
2022-12-06 07:46:51 -05:00
|
|
|
return args, osargs, nil, newBuilderError(buildxMissingError, perr)
|
2022-01-27 13:30:06 -05:00
|
|
|
}
|
|
|
|
// otherwise, display warning and continue
|
2022-12-06 07:46:51 -05:00
|
|
|
_, _ = fmt.Fprintf(dockerCli.Err(), "%s\n\n", newBuilderError(buildxMissingWarning, perr))
|
2022-11-03 17:30:27 -04:00
|
|
|
return args, osargs, nil, nil
|
2022-02-03 04:37:55 -05:00
|
|
|
}
|
|
|
|
|
2022-11-03 17:30:27 -04:00
|
|
|
// If build subcommand is forwarded, user would expect "docker build" to
|
|
|
|
// always create a local docker image (default context builder). This is
|
|
|
|
// for better backward compatibility in case where a user could switch to
|
|
|
|
// a docker container builder with "docker buildx --use foo" which does
|
2022-11-03 18:35:45 -04:00
|
|
|
// not --load by default. Also makes sure that an arbitrary builder name
|
|
|
|
// is not being set in the command line or in the environment before
|
|
|
|
// setting the default context and keep "buildx install" behavior if being
|
|
|
|
// set (builder alias).
|
|
|
|
if forwarded && !useAlias && !hasBuilderName(args, os.Environ()) {
|
2022-11-03 17:30:27 -04:00
|
|
|
envs = append([]string{"BUILDX_BUILDER=" + dockerCli.CurrentContext()}, envs...)
|
|
|
|
}
|
|
|
|
|
2024-02-13 16:40:55 -05:00
|
|
|
// overwrite the command path for this plugin using the alias name.
|
2024-03-15 11:24:05 -04:00
|
|
|
cmd.Annotations[pluginmanager.CommandAnnotationPluginCommandPath] = strings.Join(append([]string{cmd.CommandPath()}, fwcmdpath...), " ")
|
2024-02-13 16:40:55 -05:00
|
|
|
|
2022-11-03 17:30:27 -04:00
|
|
|
return fwargs, fwosargs, envs, nil
|
2022-01-27 13:30:06 -05:00
|
|
|
}
|
|
|
|
|
2024-03-15 11:24:05 -04:00
|
|
|
func forwardBuilder(alias string, args, osargs []string) ([]string, []string, []string, bool) {
|
|
|
|
aliases := [][3][]string{
|
2022-02-03 04:37:55 -05:00
|
|
|
{
|
|
|
|
{"builder"},
|
2022-01-27 13:30:06 -05:00
|
|
|
{alias},
|
2024-03-15 11:24:05 -04:00
|
|
|
{"builder"},
|
2022-02-03 04:37:55 -05:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{"build"},
|
2022-01-27 13:30:06 -05:00
|
|
|
{alias, "build"},
|
2024-03-15 11:24:05 -04:00
|
|
|
{},
|
2022-02-03 04:37:55 -05:00
|
|
|
},
|
|
|
|
{
|
|
|
|
{"image", "build"},
|
2022-01-27 13:30:06 -05:00
|
|
|
{alias, "build"},
|
2024-03-15 11:24:05 -04:00
|
|
|
{"image"},
|
2022-02-03 04:37:55 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, al := range aliases {
|
2022-01-27 13:30:06 -05:00
|
|
|
if fwargs, changed := command.StringSliceReplaceAt(args, al[0], al[1], 0); changed {
|
|
|
|
fwosargs, _ := command.StringSliceReplaceAt(osargs, al[0], al[1], -1)
|
2024-03-15 11:24:05 -04:00
|
|
|
fwcmdpath := al[2]
|
|
|
|
return fwargs, fwosargs, fwcmdpath, true
|
2022-02-03 04:37:55 -05:00
|
|
|
}
|
|
|
|
}
|
2024-03-15 11:24:05 -04:00
|
|
|
return args, osargs, nil, false
|
2022-02-03 04:37:55 -05:00
|
|
|
}
|
2022-11-03 17:30:27 -04:00
|
|
|
|
|
|
|
// hasBuilderName checks if a builder name is defined in args or env vars
|
|
|
|
func hasBuilderName(args []string, envs []string) bool {
|
|
|
|
var builder string
|
|
|
|
flagset := pflag.NewFlagSet("buildx", pflag.ContinueOnError)
|
|
|
|
flagset.Usage = func() {}
|
|
|
|
flagset.SetOutput(io.Discard)
|
|
|
|
flagset.StringVar(&builder, "builder", "", "")
|
|
|
|
_ = flagset.Parse(args)
|
|
|
|
if builder != "" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, e := range envs {
|
|
|
|
if strings.HasPrefix(e, "BUILDX_BUILDER=") && e != "BUILDX_BUILDER=" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|