Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: support for named contexts(stages) #904

Merged
merged 4 commits into from
Jan 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -564,6 +564,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