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

Dockerfile: Allow files to be excluded from cache on COPY and ADD commands #4561

Merged
12 changes: 12 additions & 0 deletions client/llb/fileop.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,18 @@ func WithAllowWildcard(b bool) RmOption {
})
}

type excludeOnCopyAction struct {
patterns []string
}

func (e *excludeOnCopyAction) SetCopyOption(i *CopyInfo) {
i.ExcludePatterns = append(i.ExcludePatterns, e.patterns...)
}

func WithExcludePatterns(patterns []string) CopyOption {
return &excludeOnCopyAction{patterns}
}

type fileActionRm struct {
file string
info RmInfo
Expand Down
2 changes: 1 addition & 1 deletion docker-bake.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ target "lint" {
matrix = {
buildtags = [
{ name = "default", tags = "", target = "golangci-lint" },
{ name = "labs", tags = "dfrunsecurity dfparents", target = "golangci-lint" },
{ name = "labs", tags = "dfrunsecurity dfparents dfexcludepatterns", target = "golangci-lint" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just linter. https://github.com/moby/buildkit/tree/master/frontend/dockerfile/release/labs needs to be updated. Your code and new test does not run atm in the CI.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

carried this

{ name = "nydus", tags = "nydus", target = "golangci-lint" },
{ name = "yaml", tags = "", target = "yamllint" },
{ name = "proto", tags = "", target = "protolint" },
Expand Down
76 changes: 43 additions & 33 deletions frontend/dockerfile/dockerfile2llb/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,17 +744,18 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
}
if err == nil {
err = dispatchCopy(d, copyConfig{
params: c.SourcesAndDest,
source: opt.buildContext,
isAddCommand: true,
cmdToPrint: c,
chown: c.Chown,
chmod: c.Chmod,
link: c.Link,
keepGitDir: c.KeepGitDir,
checksum: checksum,
location: c.Location(),
opt: opt,
params: c.SourcesAndDest,
excludePatterns: c.ExcludePatterns,
source: opt.buildContext,
isAddCommand: true,
cmdToPrint: c,
chown: c.Chown,
chmod: c.Chmod,
link: c.Link,
keepGitDir: c.KeepGitDir,
checksum: checksum,
location: c.Location(),
opt: opt,
})
}
if err == nil {
Expand Down Expand Up @@ -796,16 +797,17 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
l = src.state
}
err = dispatchCopy(d, copyConfig{
params: c.SourcesAndDest,
source: l,
isAddCommand: false,
cmdToPrint: c,
chown: c.Chown,
chmod: c.Chmod,
link: c.Link,
parents: c.Parents,
location: c.Location(),
opt: opt,
params: c.SourcesAndDest,
excludePatterns: c.ExcludePatterns,
source: l,
isAddCommand: false,
cmdToPrint: c,
chown: c.Chown,
chmod: c.Chmod,
link: c.Link,
parents: c.Parents,
location: c.Location(),
opt: opt,
})
if err == nil {
if len(cmd.sources) == 0 {
Expand Down Expand Up @@ -1128,6 +1130,13 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
copyOpt = append(copyOpt, llb.WithUser(cfg.chown))
}

if len(cfg.excludePatterns) > 0 {
// in theory we don't need to check whether there are any exclude patterns,
// as an empty list is a no-op. However, performing the check makes
// the code easier to understand and costs virtually nothing.
copyOpt = append(copyOpt, llb.WithExcludePatterns(cfg.excludePatterns))
}

var mode *os.FileMode
if cfg.chmod != "" {
p, err := strconv.ParseUint(cfg.chmod, 8, 32)
Expand Down Expand Up @@ -1329,18 +1338,19 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
}

type copyConfig struct {
params instructions.SourcesAndDest
source llb.State
isAddCommand bool
cmdToPrint fmt.Stringer
chown string
chmod string
link bool
keepGitDir bool
checksum digest.Digest
parents bool
location []parser.Range
opt dispatchOpt
params instructions.SourcesAndDest
excludePatterns []string
source llb.State
isAddCommand bool
cmdToPrint fmt.Stringer
chown string
chmod string
link bool
keepGitDir bool
checksum digest.Digest
parents bool
location []parser.Range
opt dispatchOpt
}

func dispatchMaintainer(d *dispatchState, c *instructions.MaintainerCommand) error {
Expand Down
27 changes: 27 additions & 0 deletions frontend/dockerfile/dockerfile2llb/exclude_patterns_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build dfexcludepatterns
// +build dfexcludepatterns

package dockerfile2llb

import (
"testing"

"github.com/moby/buildkit/util/appcontext"
"github.com/stretchr/testify/assert"
)

func TestDockerfileCopyExcludePatterns(t *testing.T) {
df := `FROM scratch
COPY --exclude=src/*.go --exclude=tmp/*.txt dir /sub/
`
_, _, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.NoError(t, err)
}

func TestDockerfileAddExcludePatterns(t *testing.T) {
df := `FROM scratch
ADD --exclude=src/*.go --exclude=tmp/*.txt dir /sub/
`
_, _, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.NoError(t, err)
}
1 change: 1 addition & 0 deletions frontend/dockerfile/dockerfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ func init() {

func TestIntegration(t *testing.T) {
integration.Run(t, allTests, opts...)

integration.Run(t, securityTests, append(append(opts, securityOpts...),
integration.WithMatrix("security.insecure", map[string]interface{}{
"granted": securityInsecureGranted,
Expand Down
28 changes: 19 additions & 9 deletions frontend/dockerfile/docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1138,8 +1138,8 @@ RUN apt-get update && apt-get install -y ...
ADD has two forms:

```dockerfile
ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]
ADD [--chown=<user>:<group>] [--chmod=<perms> [--exclude=<exclude>]... [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] [--chmod=<perms> [--exclude=<exclude>]... ["<src>",... "<dest>"]
```

The latter form is required for paths containing whitespace.
Expand All @@ -1158,20 +1158,25 @@ The latter form is required for paths containing whitespace.
> Only octal notation is currently supported. Non-octal support is tracked in
> [moby/buildkit#1951](https://github.com/moby/buildkit/issues/1951).

> **Note**
>
> The `--exclude` option can be specified multiple times and cause files matching its patterns not to be copied,
> even if the files paths match the pattern specified in `<src>`. This feature requires the build tag `dfexcludepatterns`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"This feature requires using labs channel"

cc @dvdksn to double check this before the release. Maybe we need to separate it more. I also see that --parents is not mentioned for COPY that should be similar.


The `ADD` instruction copies new files, directories or remote file URLs from `<src>`
and adds them to the filesystem of the image at the path `<dest>`.

Multiple `<src>` resources may be specified but if they are files or
directories, their paths are interpreted as relative to the source of
the context of the build.

Each `<src>` may contain wildcards and matching will be done using Go's
Each `<src>` and `<exclude>` may contain wildcards and matching will be done using Go's
[filepath.Match](https://golang.org/pkg/path/filepath#Match) rules. For example:

To add all files starting with "hom":
To add all files starting with "hom", excluding all files with `.txt` and `.md` extensions:

```dockerfile
ADD hom* /mydir/
ADD --exclude=*.txt --exclude=*.md hom* /mydir/
```

In the example below, `?` is replaced with any single character, e.g., "home.txt".
Expand Down Expand Up @@ -1366,8 +1371,8 @@ See [`COPY --link`](#copy---link).
COPY has two forms:

```dockerfile
COPY [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest>
COPY [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]
COPY [--chown=<user>:<group>] [--chmod=<perms>] [--exclude=<exclude>]... <src>... <dest>
COPY [--chown=<user>:<group>] [--chmod=<perms>] [--exclude=<exclude>]... ["<src>",... "<dest>"]
```

This latter form is required for paths containing whitespace
Expand All @@ -1380,6 +1385,11 @@ This latter form is required for paths containing whitespace
> translating user and group names to IDs restricts this feature to only be viable for
> Linux OS-based containers.

> **Note**
>
> The `--exclude` option can be specified multiple times and cause files matching its patterns not to be copied,
> even if the files paths match the pattern specified in `<src>`. This feature requires the build tag `dfexcludepatterns`.

The `COPY` instruction copies new files or directories from `<src>`
and adds them to the filesystem of the container at the path `<dest>`.

Expand All @@ -1390,10 +1400,10 @@ of the build.
Each `<src>` may contain wildcards and matching will be done using Go's
[filepath.Match](https://golang.org/pkg/path/filepath#Match) rules. For example:

To add all files starting with "hom":
To add all files starting with "hom", excluding all files with `.txt` and `.md` extensions:

```dockerfile
COPY hom* /mydir/
COPY --exclude=*.txt --exclude=*.md hom* /mydir/
```

In the example below, `?` is replaced with any single character, e.g., "home.txt".
Expand Down
Loading
Loading