Skip to content

Commit

Permalink
Merge pull request #904 from tonistiigi/named-contexts
Browse files Browse the repository at this point in the history
build: support for named contexts(stages)
  • Loading branch information
tonistiigi authored Jan 21, 2022
2 parents 14b38a9 + 0854782 commit 11b771c
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 0 deletions.
23 changes: 23 additions & 0 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"sync"
"syscall"
"time"

"github.com/containerd/containerd/images"
Expand Down Expand Up @@ -79,6 +80,7 @@ type Inputs struct {
InStream io.Reader
ContextState *llb.State
DockerfileInline string
NamedContexts map[string]string
}

type DriverInfo struct {
Expand Down Expand Up @@ -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 build context %v", k)
}
if !st.IsDir() {
return nil, errors.Wrapf(syscall.ENOTDIR, "failed to get build 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)
Expand Down
59 changes: 59 additions & 0 deletions commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ 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"
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/morikuni/aec"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"google.golang.org/grpc/codes"
)

const defaultTargetName = "default"
Expand All @@ -44,6 +47,7 @@ type buildOptions struct {
cacheFrom []string
cacheTo []string
cgroupParent string
contexts []string
extraHosts []string
imageIDFile string
labels []string
Expand Down Expand Up @@ -100,11 +104,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,
Expand Down Expand Up @@ -209,6 +219,7 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
}

imageID, err := buildTargets(ctx, dockerCli, map[string]build.Options{defaultTargetName: opts}, in.progress, contextPathHash, in.builder, in.metadataFile)
err = wrapBuildError(err)
if err != nil {
return err
}
Expand Down Expand Up @@ -339,6 +350,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, "build-context", []string{}, "Additional build 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"})

Expand Down Expand Up @@ -462,3 +475,49 @@ 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
}

func wrapBuildError(err error) error {
if err == nil {
return nil
}
st, ok := grpcerrors.AsGRPCStatus(err)
if ok {
if st.Code() == codes.Unimplemented && strings.Contains(st.Message(), "unsupported frontend capability moby.buildkit.frontend.contexts") {
return &wrapped{err, "current frontend does not support --build-context. Named contexts are supported since Dockerfile v1.4"}
}
}
return err
}

type wrapped struct {
err error
msg string
}

func (w *wrapped) Error() string {
return w.msg
}

func (w *wrapped) Unwrap() error {
return w.err
}
34 changes: 34 additions & 0 deletions docs/reference/buildx_build.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Start a build
| [`--add-host stringSlice`](https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host) | Add a custom host-to-IP mapping (format: `host:ip`) |
| [`--allow stringSlice`](#allow) | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
| [`--build-arg stringArray`](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg) | Set build-time variables |
| [`--build-context stringArray`](#build-context) | Additional build contexts (e.g., name=path) |
| [`--builder string`](#builder) | Override the configured builder instance |
| [`--cache-from stringArray`](#cache-from) | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
| [`--cache-to stringArray`](#cache-to) | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
Expand Down Expand Up @@ -303,6 +304,39 @@ $ docker buildx build --cache-to=type=gha .

More info about cache exporters and available attributes: https://github.com/moby/buildkit#export-cache


### <a name="build-context"></a> Additional build contexts (--build-context)

```
--build-context=name=VALUE
```

Define additional build context with specified contents. In Dockerfile the context can be accessed when `FROM name` or `--from=name` is used.
When Dockerfile defines a stage with the same name it is overwritten.

The value can be a local source directory, container image (with docker-image:// prefix), Git or HTTP URL.

**Examples**

Replace `alpine:latest` with a pinned one:

```console
$ docker buildx build --build-context alpine=docker-image://alpine@sha256:0123456789 .
```

Expose a secondary local source directory:

```console
$ docker buildx build --build-context project=path/to/project/source .
# docker buildx build --build-context project=https://github.com/myuser/project.git .
```

```Dockerfile
FROM alpine
COPY --from=project myfile /
```


### <a name="allow"></a> Allow extra privileged entitlement (--allow)

```
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ require (
go.opentelemetry.io/otel v1.2.0
go.opentelemetry.io/otel/trace v1.2.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
google.golang.org/grpc v1.42.0
gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect
gopkg.in/fatih/pool.v2 v2.0.0 // indirect
gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect
Expand Down
1 change: 1 addition & 0 deletions vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ google.golang.org/genproto/googleapis/rpc/errdetails
google.golang.org/genproto/googleapis/rpc/status
google.golang.org/genproto/protobuf/field_mask
# google.golang.org/grpc v1.42.0
## explicit
google.golang.org/grpc
google.golang.org/grpc/attributes
google.golang.org/grpc/backoff
Expand Down

0 comments on commit 11b771c

Please sign in to comment.