Skip to content

Commit

Permalink
Adds a rule check for copying files which match the .dockerignore pat…
Browse files Browse the repository at this point in the history
…terns

Signed-off-by: Talon Bowler <talon.bowler@docker.com>
  • Loading branch information
daghack committed Jul 8, 2024
1 parent 1aee151 commit 735cbb4
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 42 deletions.
5 changes: 5 additions & 0 deletions frontend/dockerfile/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
return nil, capsError
}

dockerIgnorePatterns, err := bc.DockerIgnorePatterns(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to parse ignore patterns")
}
convertOpt := dockerfile2llb.ConvertOpt{
Config: bc.Config,
Client: bc,
Expand All @@ -84,6 +88,7 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
msg = linter.LintFormatShort(rulename, msg, startLine)
src.Warn(ctx, msg, warnOpts(location, [][]byte{[]byte(description)}, url))
},
DockerIgnorePatterns: dockerIgnorePatterns,
}

if res, ok, err := bc.HandleSubrequest(ctx, dockerui.RequestHandler{
Expand Down
37 changes: 34 additions & 3 deletions frontend/dockerfile/dockerfile2llb/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/containerd/platforms"
"github.com/distribution/reference"
"github.com/docker/go-connections/nat"
"github.com/moby/patternmatcher"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/llb/imagemetaresolver"
"github.com/moby/buildkit/client/llb/sourceresolver"
Expand Down Expand Up @@ -75,6 +76,7 @@ type ConvertOpt struct {
LLBCaps *apicaps.CapSet
Warn linter.LintWarnFunc
AllStages bool
DockerIgnorePatterns []string
}

type SBOMTargets struct {
Expand Down Expand Up @@ -630,7 +632,13 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
return nil, parser.WithLocation(err, d.stage.Location)
}
}

d.state = d.state.Network(opt.NetworkMode)
dockerIgnorePatterns, err := opt.Client.DockerIgnorePatterns(ctx)
if err != nil {
return nil, err
}

opt := dispatchOpt{
allDispatchStates: allDispatchStates,
metaArgs: optMetaArgs,
Expand All @@ -648,6 +656,7 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
llbCaps: opt.LLBCaps,
sourceMap: opt.SourceMap,
lint: lint,
dockerIgnorePatterns: dockerIgnorePatterns,
}

if err = dispatchOnBuildTriggers(d, d.image.Config.OnBuild, opt); err != nil {
Expand Down Expand Up @@ -822,6 +831,7 @@ type dispatchOpt struct {
llbCaps *apicaps.CapSet
sourceMap *llb.SourceMap
lint *linter.Linter
dockerIgnorePatterns []string
}

func getEnv(state llb.State) shell.EnvGetter {
Expand Down Expand Up @@ -909,7 +919,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
checksum: checksum,
location: c.Location(),
opt: opt,
})
}, opt.lint)
}
if err == nil {
for _, src := range c.SourcePaths {
Expand Down Expand Up @@ -961,7 +971,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
parents: c.Parents,
location: c.Location(),
opt: opt,
})
}, opt.lint)
if err == nil {
if len(cmd.sources) == 0 {
for _, src := range c.SourcePaths {
Expand Down Expand Up @@ -1313,7 +1323,7 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bo
return nil
}

func dispatchCopy(d *dispatchState, cfg copyConfig) error {
func dispatchCopy(d *dispatchState, cfg copyConfig, lint *linter.Linter) error {
dest, err := pathRelativeToWorkingDir(d.state, cfg.params.DestPath, *d.platform)
if err != nil {
return err
Expand Down Expand Up @@ -1381,6 +1391,8 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {

var a *llb.FileAction

validateCopySourcePath(cfg.opt.dockerIgnorePatterns, cfg.params.SourcePaths, cfg.location, lint)

for _, src := range cfg.params.SourcePaths {
commitMessage.WriteString(" " + src)
gitRef, gitRefErr := gitutil.ParseGitRef(src)
Expand Down Expand Up @@ -1867,6 +1879,25 @@ func addReachableStages(s *dispatchState, stages map[*dispatchState]struct{}) {
}
}

func validateCopySourcePath(excludePatterns, paths []string, location []parser.Range, lint *linter.Linter) error {
matcher, err := patternmatcher.New(excludePatterns)
if err != nil {
return err
}
for _, src := range paths {

ok, err := matcher.MatchesOrParentMatches(src)
if err != nil {
return err
}
if ok {
msg := linter.RuleCopyIgnoredFile.Format(src)
lint.Run(&linter.RuleCopyIgnoredFile, location, msg)
}
}
return nil
}

func validateCircularDependency(states []*dispatchState) error {
var visit func(*dispatchState, []instructions.Command) []instructions.Command
if states == nil {
Expand Down
8 changes: 8 additions & 0 deletions frontend/dockerfile/linter/ruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,12 @@ var (
return fmt.Sprintf("Default value for ARG %v results in empty or invalid base image name", baseName)
},
}
RuleCopyIgnoredFile = LinterRule[func(string) string]{
Name: "CopyIgnoredFile",
Description: "File is ignored by .dockerignore",
URL: "",
Format: func(file string) string {
return fmt.Sprintf("File %q is ignored by .dockerignore", file)
},
}
)
92 changes: 53 additions & 39 deletions frontend/dockerui/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,45 +410,7 @@ func (bc *Client) MainContext(ctx context.Context, opts ...llb.LocalOption) (*ll
return bctx.context, nil
}

if bc.dockerignore == nil {
st := llb.Local(bctx.contextLocalName,
llb.SessionID(bc.bopts.SessionID),
llb.FollowPaths([]string{DefaultDockerignoreName}),
llb.SharedKeyHint(bctx.contextLocalName+"-"+DefaultDockerignoreName),
WithInternalName("load "+DefaultDockerignoreName),
llb.Differ(llb.DiffNone, false),
)
def, err := st.Marshal(ctx, bc.marshalOpts()...)
if err != nil {
return nil, err
}
res, err := bc.client.Solve(ctx, client.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, err
}
ref, err := res.SingleRef()
if err != nil {
return nil, err
}
dt, _ := ref.ReadFile(ctx, client.ReadRequest{ // ignore error
Filename: DefaultDockerignoreName,
})
if dt == nil {
dt = []byte{}
}
bc.dockerignore = dt
bc.dockerignoreName = DefaultDockerignoreName
}

var excludes []string
if len(bc.dockerignore) != 0 {
excludes, err = ignorefile.ReadAll(bytes.NewBuffer(bc.dockerignore))
if err != nil {
return nil, errors.Wrapf(err, "failed parsing %s", bc.dockerignoreName)
}
}
excludes, err := bc.dockerIgnoreExcludes(ctx, bctx)

opts = append([]llb.LocalOption{
llb.SessionID(bc.bopts.SessionID),
Expand Down Expand Up @@ -493,6 +455,15 @@ func (bc *Client) IsNoCache(name string) bool {
return false
}

func (bc *Client) DockerIgnorePatterns(ctx context.Context) ([]string, error) {
bctx, err := bc.buildContext(ctx)
if err != nil {
return nil, err
}

return bc.dockerIgnoreExcludes(ctx, bctx)
}

func DefaultMainContext(opts ...llb.LocalOption) *llb.State {
opts = append([]llb.LocalOption{
llb.SharedKeyHint(DefaultLocalNameContext),
Expand All @@ -505,3 +476,46 @@ func DefaultMainContext(opts ...llb.LocalOption) *llb.State {
func WithInternalName(name string) llb.ConstraintsOpt {
return llb.WithCustomName("[internal] " + name)
}

func (bc *Client) dockerIgnoreExcludes(ctx context.Context, bctx *buildContext) ([]string, error) {
if bc.dockerignore == nil {
st := llb.Local(bctx.contextLocalName,
llb.SessionID(bc.bopts.SessionID),
llb.FollowPaths([]string{DefaultDockerignoreName}),
llb.SharedKeyHint(bctx.contextLocalName+"-"+DefaultDockerignoreName),
WithInternalName("load "+DefaultDockerignoreName),
llb.Differ(llb.DiffNone, false),
)
def, err := st.Marshal(ctx, bc.marshalOpts()...)
if err != nil {
return nil, err
}
res, err := bc.client.Solve(ctx, client.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, err
}
ref, err := res.SingleRef()
if err != nil {
return nil, err
}
dt, _ := ref.ReadFile(ctx, client.ReadRequest{ // ignore error
Filename: DefaultDockerignoreName,
})
if dt == nil {
dt = []byte{}
}
bc.dockerignore = dt
bc.dockerignoreName = DefaultDockerignoreName
}
var err error
var excludes []string
if len(bc.dockerignore) != 0 {
excludes, err = ignorefile.ReadAll(bytes.NewBuffer(bc.dockerignore))
if err != nil {
return nil, errors.Wrapf(err, "failed parsing %s", bc.dockerignoreName)
}
}
return excludes, nil
}

0 comments on commit 735cbb4

Please sign in to comment.