From de6b04d7266320b42abdb1a55b2173446003f94a Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 15 Dec 2021 21:02:31 -0800 Subject: [PATCH] build: add support for named contexts Signed-off-by: Tonis Tiigi --- build/build.go | 23 +++++++++++++++++++++++ commands/build.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/build/build.go b/build/build.go index 1783e087..d0b77b0a 100644 --- a/build/build.go +++ b/build/build.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" "sync" + "syscall" "time" "github.com/containerd/containerd/images" @@ -79,6 +80,7 @@ type Inputs struct { InStream io.Reader ContextState *llb.State DockerfileInline string + NamedContexts map[string]string } type DriverInfo struct { @@ -1080,6 +1082,27 @@ func LoadInputs(ctx context.Context, d driver.Driver, inp Inputs, pw progress.Wr target.FrontendAttrs["filename"] = dockerfileName + for k, v := range inp.NamedContexts { + target.FrontendAttrs["frontend.caps"] = "moby.buildkit.frontend.contexts+forward" + if urlutil.IsGitURL(v) || urlutil.IsURL(v) || strings.HasPrefix(v, "docker-image://") { + target.FrontendAttrs["context:"+k] = v + continue + } + st, err := os.Stat(v) + if err != nil { + return nil, errors.Wrapf(err, "failed to get context %v", k) + } + if !st.IsDir() { + return nil, errors.Wrapf(syscall.ENOTDIR, "failed to get context path %v", v) + } + localName := k + if k == "context" || k == "dockerfile" { + localName = "_" + k // underscore to avoid collisions + } + target.LocalDirs[localName] = v + target.FrontendAttrs["context:"+k] = "local:" + localName + } + release := func() { for _, dir := range toRemove { os.RemoveAll(dir) diff --git a/commands/build.go b/commands/build.go index dae6d326..96df9202 100644 --- a/commands/build.go +++ b/commands/build.go @@ -20,6 +20,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" dockeropts "github.com/docker/cli/opts" + "github.com/docker/distribution/reference" "github.com/docker/docker/pkg/ioutils" "github.com/docker/go-units" "github.com/moby/buildkit/client" @@ -44,6 +45,7 @@ type buildOptions struct { cacheFrom []string cacheTo []string cgroupParent string + contexts []string extraHosts []string imageIDFile string labels []string @@ -100,11 +102,17 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) { in.progress = "quiet" } + contexts, err := parseContextNames(in.contexts) + if err != nil { + return err + } + opts := build.Options{ Inputs: build.Inputs{ ContextPath: in.contextPath, DockerfilePath: in.dockerfileName, InStream: os.Stdin, + NamedContexts: contexts, }, BuildArgs: listToMap(in.buildArgs, true), ExtraHosts: in.extraHosts, @@ -339,6 +347,8 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { flags.StringVar(&options.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container") flags.SetAnnotation("cgroup-parent", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent"}) + flags.StringArrayVar(&options.contexts, "context", []string{}, "Additional named contexts (e.g., name=path)") + flags.StringVarP(&options.dockerfileName, "file", "f", "", `Name of the Dockerfile (default: "PATH/Dockerfile")`) flags.SetAnnotation("file", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f"}) @@ -462,3 +472,23 @@ func listToMap(values []string, defaultEnv bool) map[string]string { } return result } + +func parseContextNames(values []string) (map[string]string, error) { + if len(values) == 0 { + return nil, nil + } + result := make(map[string]string, len(values)) + for _, value := range values { + kv := strings.SplitN(value, "=", 2) + if len(kv) != 2 { + return nil, errors.Errorf("invalid context value: %s, expected key=value", value) + } + named, err := reference.ParseNormalizedNamed(kv[0]) + if err != nil { + return nil, errors.Wrapf(err, "invalid context name %s", kv[0]) + } + name := strings.TrimSuffix(reference.FamiliarString(named), ":latest") + result[name] = kv[1] + } + return result, nil +}