Skip to content

Commit 17013a5

Browse files
authored
Merge pull request #466 from dgageot/paths
Improve Docker dependencies code
2 parents 975c0b0 + 7468876 commit 17013a5

File tree

9 files changed

+141
-272
lines changed

9 files changed

+141
-272
lines changed

pkg/skaffold/bazel/bazel.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package bazel
1919
import (
2020
"fmt"
2121
"os/exec"
22-
"path/filepath"
2322
"strings"
2423

2524
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
@@ -52,9 +51,7 @@ func (*BazelDependencyResolver) GetDependencies(a *v1alpha2.Artifact) ([]string,
5251
continue
5352
}
5453

55-
dep := filepath.Join(a.Workspace, depToPath(l))
56-
57-
deps = append(deps, dep)
54+
deps = append(deps, depToPath(l))
5855
}
5956
return deps, nil
6057
}

pkg/skaffold/build/deps.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package build
1818

1919
import (
2020
"fmt"
21+
"path/filepath"
2122
"sort"
2223
"strings"
2324

@@ -119,7 +120,7 @@ func pathsForArtifact(a *v1alpha2.Artifact) ([]string, error) {
119120
logrus.Debugf("Ignoring %s for artifact dependencies", dep)
120121
continue
121122
}
122-
filteredDeps = append(filteredDeps, dep)
123+
filteredDeps = append(filteredDeps, filepath.Join(a.Workspace, dep))
123124
}
124125
return filteredDeps, nil
125126
}

pkg/skaffold/docker/context.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,8 @@ import (
2525
"github.com/pkg/errors"
2626
)
2727

28-
func contextTarPaths(dockerfilePath string, context string) ([]string, error) {
29-
return GetDockerfileDependencies(dockerfilePath, context)
30-
}
31-
3228
func CreateDockerTarContext(w io.Writer, dockerfilePath, context string) error {
33-
paths, err := contextTarPaths(dockerfilePath, context)
29+
paths, err := GetDockerfileDependencies(dockerfilePath, context)
3430
if err != nil {
3531
return errors.Wrap(err, "getting relative tar paths")
3632
}
@@ -41,7 +37,7 @@ func CreateDockerTarContext(w io.Writer, dockerfilePath, context string) error {
4137
}
4238

4339
func CreateDockerTarGzContext(w io.Writer, dockerfilePath, context string) error {
44-
paths, err := contextTarPaths(dockerfilePath, context)
40+
paths, err := GetDockerfileDependencies(dockerfilePath, context)
4541
if err != nil {
4642
return errors.Wrap(err, "getting relative tar paths")
4743
}

pkg/skaffold/docker/context_test.go

+29-17
Original file line numberDiff line numberDiff line change
@@ -19,51 +19,63 @@ package docker
1919
import (
2020
"archive/tar"
2121
"io"
22+
"io/ioutil"
23+
"os"
24+
"path/filepath"
2225
"testing"
2326

24-
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
25-
"github.com/pkg/errors"
27+
"github.com/GoogleContainerTools/skaffold/testutil"
2628
)
2729

2830
func TestDockerContext(t *testing.T) {
31+
tmpDir, cleanup := testutil.TempDir(t)
32+
defer cleanup()
33+
34+
os.Mkdir(filepath.Join(tmpDir, "files"), 0750)
35+
ioutil.WriteFile(filepath.Join(tmpDir, "files", "ignored.txt"), []byte(""), 0644)
36+
ioutil.WriteFile(filepath.Join(tmpDir, "files", "included.txt"), []byte(""), 0644)
37+
ioutil.WriteFile(filepath.Join(tmpDir, ".dockerignore"), []byte("**/ignored.txt\nalsoignored.txt"), 0644)
38+
ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte("FROM alpine\nCOPY ./files /files"), 0644)
39+
ioutil.WriteFile(filepath.Join(tmpDir, "ignored.txt"), []byte(""), 0644)
40+
ioutil.WriteFile(filepath.Join(tmpDir, "alsoignored.txt"), []byte(""), 0644)
41+
2942
reader, writer := io.Pipe()
3043
go func() {
31-
err := CreateDockerTarContext(writer, "Dockerfile", "../../../testdata/docker")
44+
err := CreateDockerTarContext(writer, "Dockerfile", tmpDir)
3245
if err != nil {
33-
writer.CloseWithError(errors.Wrap(err, "creating docker context"))
34-
panic(err)
46+
writer.CloseWithError(err)
47+
} else {
48+
writer.Close()
3549
}
36-
writer.Close()
3750
}()
3851

39-
var files []string
52+
files := make(map[string]bool)
4053
tr := tar.NewReader(reader)
4154
for {
4255
header, err := tr.Next()
43-
4456
if err == io.EOF {
4557
break
4658
}
4759
if err != nil {
48-
panic(errors.Wrap(err, "reading tar headers"))
60+
t.Fatal(err)
4961
}
5062

51-
files = append(files, header.Name)
63+
files[header.Name] = true
5264
}
5365

54-
if util.StrSliceContains(files, "ignored.txt") {
66+
if files["ignored.txt"] {
5567
t.Error("File ignored.txt should have been excluded, but was not")
5668
}
57-
58-
if util.StrSliceContains(files, "files/ignored.txt") {
69+
if files["alsoignored.txt"] {
70+
t.Error("File alsoignored.txt should have been excluded, but was not")
71+
}
72+
if files["files/ignored.txt"] {
5973
t.Error("File files/ignored.txt should have been excluded, but was not")
6074
}
61-
62-
if !util.StrSliceContains(files, "files/included.txt") {
75+
if !files["files/included.txt"] {
6376
t.Error("File files/included.txt should have been included, but was not")
6477
}
65-
66-
if !util.StrSliceContains(files, "Dockerfile") {
78+
if !files["Dockerfile"] {
6779
t.Error("File Dockerfile should have been included, but was not")
6880
}
6981
}

pkg/skaffold/docker/parse.go

+72-60
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,12 @@ import (
2222
"fmt"
2323
"io"
2424
"os"
25-
"path"
2625
"path/filepath"
2726
"sort"
2827
"strings"
2928
"sync"
3029

3130
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
32-
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
3331
"github.com/google/go-containerregistry/v1"
3432

3533
"github.com/docker/docker/builder/dockerignore"
@@ -57,11 +55,9 @@ func (d *DockerfileDepResolver) GetDependencies(a *v1alpha2.Artifact) ([]string,
5755
return GetDockerfileDependencies(a.DockerArtifact.DockerfilePath, a.Workspace)
5856
}
5957

60-
// GetDockerfileDependencies parses a dockerfile and returns the full paths
61-
// of all the source files that the resulting docker image depends on.
62-
func GetDockerfileDependencies(dockerfilePath, workspace string) ([]string, error) {
58+
func readDockerfile(workspace, dockerfilePath string) ([]string, error) {
6359
path := filepath.Join(workspace, dockerfilePath)
64-
f, err := util.Fs.Open(path)
60+
f, err := os.Open(path)
6561
if err != nil {
6662
return nil, errors.Wrapf(err, "opening dockerfile: %s", path)
6763
}
@@ -91,7 +87,7 @@ func GetDockerfileDependencies(dockerfilePath, workspace string) ([]string, erro
9187
for _, value := range r.AST.Children {
9288
switch value.Value {
9389
case add, copy:
94-
processCopy(workspace, value, depMap, envs)
90+
processCopy(value, depMap, envs)
9591
case env:
9692
envs[value.Next.Value] = value.Next.Next.Value
9793
}
@@ -109,30 +105,86 @@ func GetDockerfileDependencies(dockerfilePath, workspace string) ([]string, erro
109105

110106
dispatchInstructions(res)
111107

112-
deps := []string{}
108+
var deps []string
113109
for dep := range depMap {
114110
deps = append(deps, dep)
115111
}
116112
logrus.Infof("Found dependencies for dockerfile %s", deps)
117113

118-
expandedDeps, err := util.ExpandPaths(workspace, deps)
114+
return deps, nil
115+
}
116+
117+
func GetDockerfileDependencies(dockerfilePath, workspace string) ([]string, error) {
118+
deps, err := readDockerfile(workspace, dockerfilePath)
119119
if err != nil {
120-
return nil, errors.Wrap(err, "expanding dockerfile paths")
120+
return nil, err
121121
}
122-
logrus.Infof("deps %s", expandedDeps)
123122

124-
if !util.StrSliceContains(expandedDeps, path) {
125-
expandedDeps = append(expandedDeps, path)
123+
// Read patterns to ignore
124+
var excludes []string
125+
dockerignorePath := filepath.Join(workspace, ".dockerignore")
126+
if _, err := os.Stat(dockerignorePath); !os.IsNotExist(err) {
127+
r, err := os.Open(dockerignorePath)
128+
if err != nil {
129+
return nil, err
130+
}
131+
defer r.Close()
132+
133+
excludes, err = dockerignore.ReadAll(r)
134+
if err != nil {
135+
return nil, err
136+
}
137+
}
138+
139+
// Walk the workspace
140+
files := make(map[string]bool)
141+
for _, dep := range deps {
142+
filepath.Walk(filepath.Join(workspace, dep), func(fpath string, info os.FileInfo, err error) error {
143+
if err != nil {
144+
return err
145+
}
146+
147+
relPath, err := filepath.Rel(workspace, fpath)
148+
if err != nil {
149+
return err
150+
}
151+
152+
ignored, err := fileutils.Matches(relPath, excludes)
153+
if err != nil {
154+
return err
155+
}
156+
157+
if info.IsDir() && ignored {
158+
return filepath.SkipDir
159+
}
160+
161+
if !info.IsDir() && !ignored {
162+
files[relPath] = true
163+
}
164+
165+
return nil
166+
})
126167
}
127168

128-
// Look for .dockerignore.
129-
ignorePath := filepath.Join(workspace, ".dockerignore")
130-
filteredDeps, err := ApplyDockerIgnore(expandedDeps, ignorePath)
169+
// Add dockerfile?
170+
m, err := fileutils.Matches(dockerfilePath, excludes)
131171
if err != nil {
132-
return nil, errors.Wrap(err, "applying dockerignore")
172+
return nil, err
173+
}
174+
if !m {
175+
files[dockerfilePath] = true
176+
}
177+
178+
// Ignore .dockerignore
179+
delete(files, ".dockerignore")
180+
181+
var dependencies []string
182+
for file := range files {
183+
dependencies = append(dependencies, file)
133184
}
185+
sort.Strings(dependencies)
134186

135-
return filteredDeps, nil
187+
return dependencies, nil
136188
}
137189

138190
func PortsFromDockerfile(r io.Reader) ([]string, error) {
@@ -242,7 +294,7 @@ func retrieveRemoteConfig(identifier string) (*v1.ConfigFile, error) {
242294
return img.ConfigFile()
243295
}
244296

245-
func processCopy(workspace string, value *parser.Node, paths map[string]struct{}, envs map[string]string) error {
297+
func processCopy(value *parser.Node, paths map[string]struct{}, envs map[string]string) error {
246298
slex := shell.NewLex('\\')
247299
for {
248300
// Skip last node, since it is the destination, and stop if we arrive at a comment
@@ -259,8 +311,7 @@ func processCopy(workspace string, value *parser.Node, paths map[string]struct{}
259311
return nil
260312
}
261313
if !strings.HasPrefix(src, "http://") && !strings.HasPrefix(src, "https://") {
262-
dep := path.Join(workspace, src)
263-
paths[dep] = struct{}{}
314+
paths[src] = struct{}{}
264315
} else {
265316
logrus.Debugf("Skipping watch on remote dependency %s", src)
266317
}
@@ -286,42 +337,3 @@ func hasMultiStageFlag(flags []string) bool {
286337
}
287338
return false
288339
}
289-
290-
func ApplyDockerIgnore(paths []string, dockerIgnorePath string) ([]string, error) {
291-
absPaths, err := util.RelPathToAbsPath(paths)
292-
if err != nil {
293-
return nil, errors.Wrap(err, "getting absolute path of dependencies")
294-
}
295-
excludes := []string{}
296-
if _, err := util.Fs.Stat(dockerIgnorePath); !os.IsNotExist(err) {
297-
r, err := util.Fs.Open(dockerIgnorePath)
298-
if err != nil {
299-
return nil, err
300-
}
301-
defer r.Close()
302-
303-
excludes, err = dockerignore.ReadAll(r)
304-
if err != nil {
305-
return nil, err
306-
}
307-
excludes = append(excludes, ".dockerignore")
308-
}
309-
310-
absPathExcludes, err := util.RelPathToAbsPath(excludes)
311-
if err != nil {
312-
return nil, errors.Wrap(err, "getting absolute path of docker ignored paths")
313-
}
314-
315-
filteredDeps := []string{}
316-
for _, d := range absPaths {
317-
m, err := fileutils.Matches(d, absPathExcludes)
318-
if err != nil {
319-
return nil, err
320-
}
321-
if !m {
322-
filteredDeps = append(filteredDeps, d)
323-
}
324-
}
325-
sort.Strings(filteredDeps)
326-
return filteredDeps, nil
327-
}

0 commit comments

Comments
 (0)