Skip to content

Commit fb3d1bf

Browse files
antoinegellozThibaut-gauvin
authored andcommitted
fix: GenerateDAG: prevent using duplicate name labels in Dockerfiles
1 parent a1cc197 commit fb3d1bf

File tree

5 files changed

+57
-30
lines changed

5 files changed

+57
-30
lines changed

pkg/dib/generate_dag.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ func GenerateDAG(buildPath, registryPrefix, customHashListPath string, buildArgs
7575
}
7676
img.IgnorePatterns = ignorePatterns
7777

78+
if n, ok := cache[img.Name]; ok {
79+
return fmt.Errorf("duplicate image name %q found while reading file %q: previous file was %q",
80+
img.Name, filePath, path.Join(n.Image.Dockerfile.ContextPath, n.Image.Dockerfile.Filename))
81+
}
82+
7883
allParents[img.Name] = dckfile.From
7984
cache[img.Name] = dag.NewNode(img)
8085
}

pkg/dib/generate_dag_test.go

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dib_test
22

33
import (
4+
"fmt"
45
"os"
56
"os/exec"
67
"path"
@@ -12,13 +13,16 @@ import (
1213
"github.com/stretchr/testify/require"
1314
)
1415

15-
const fixtureDir = "../../test/fixtures/docker"
16+
const (
17+
buildPath1 = "../../test/fixtures/docker"
18+
buildPath2 = "../../test/fixtures/docker-duplicates"
19+
registryPrefix = "eu.gcr.io/my-test-repository"
20+
)
1621

1722
//nolint:paralleltest
1823
func TestGenerateDAG(t *testing.T) {
1924
t.Run("basic tests", func(t *testing.T) {
20-
graph, err := dib.GenerateDAG(fixtureDir,
21-
"eu.gcr.io/my-test-repository", "", map[string]string{})
25+
graph, err := dib.GenerateDAG(buildPath1, registryPrefix, "", nil)
2226
require.NoError(t, err)
2327

2428
nodes := flattenNodes(graph)
@@ -27,7 +31,7 @@ func TestGenerateDAG(t *testing.T) {
2731
multistageNode := nodes["multistage"]
2832

2933
rootImage := rootNode.Image
30-
assert.Equal(t, "eu.gcr.io/my-test-repository/bullseye", rootImage.Name)
34+
assert.Equal(t, path.Join(registryPrefix, "bullseye"), rootImage.Name)
3135
assert.Equal(t, "bullseye", rootImage.ShortName)
3236
assert.Empty(t, rootNode.Parents())
3337
assert.Len(t, rootNode.Children(), 3)
@@ -37,10 +41,9 @@ func TestGenerateDAG(t *testing.T) {
3741
})
3842

3943
t.Run("modifying the root node should change all hashes", func(t *testing.T) {
40-
tmpDir := copyFixtures(t)
44+
buildPath := copyFixtures(t, buildPath1)
4145

42-
graph0, err := dib.GenerateDAG(tmpDir,
43-
"eu.gcr.io/my-test-repository", "", map[string]string{})
46+
graph0, err := dib.GenerateDAG(buildPath, registryPrefix, "", nil)
4447
require.NoError(t, err)
4548

4649
nodes0 := flattenNodes(graph0)
@@ -50,13 +53,12 @@ func TestGenerateDAG(t *testing.T) {
5053

5154
// When I add a new file in bullseye/ (root node)
5255
require.NoError(t, os.WriteFile(
53-
path.Join(tmpDir, "bullseye/newfile"),
56+
path.Join(buildPath, "bullseye/newfile"),
5457
[]byte("any content"),
5558
os.ModePerm))
5659

5760
// Then ONLY the hash of the child node bullseye/multistage should have changed
58-
graph1, err := dib.GenerateDAG(tmpDir,
59-
"eu.gcr.io/my-test-repository", "", map[string]string{})
61+
graph1, err := dib.GenerateDAG(buildPath, registryPrefix, "", nil)
6062
require.NoError(t, err)
6163

6264
nodes1 := flattenNodes(graph1)
@@ -70,10 +72,9 @@ func TestGenerateDAG(t *testing.T) {
7072
})
7173

7274
t.Run("modifying a child node should change only its hash", func(t *testing.T) {
73-
tmpDir := copyFixtures(t)
75+
buildPath := copyFixtures(t, buildPath1)
7476

75-
graph0, err := dib.GenerateDAG(tmpDir,
76-
"eu.gcr.io/my-test-repository", "", map[string]string{})
77+
graph0, err := dib.GenerateDAG(buildPath, registryPrefix, "", nil)
7778
require.NoError(t, err)
7879

7980
nodes0 := flattenNodes(graph0)
@@ -83,13 +84,12 @@ func TestGenerateDAG(t *testing.T) {
8384

8485
// When I add a new file in bullseye/multistage/ (child node)
8586
require.NoError(t, os.WriteFile(
86-
path.Join(tmpDir, "bullseye/multistage/newfile"),
87+
path.Join(buildPath, "bullseye/multistage/newfile"),
8788
[]byte("file contents"),
8889
os.ModePerm))
8990

9091
// Then ONLY the hash of the child node bullseye/multistage should have changed
91-
graph1, err := dib.GenerateDAG(tmpDir,
92-
"eu.gcr.io/my-test-repository", "", map[string]string{})
92+
graph1, err := dib.GenerateDAG(buildPath, registryPrefix, "", nil)
9393
require.NoError(t, err)
9494

9595
nodes1 := flattenNodes(graph1)
@@ -103,14 +103,11 @@ func TestGenerateDAG(t *testing.T) {
103103
})
104104

105105
t.Run("using custom hash list should change only hashes of nodes with custom label", func(t *testing.T) {
106-
graph0, err := dib.GenerateDAG(fixtureDir,
107-
"eu.gcr.io/my-test-repository", "", map[string]string{})
106+
graph0, err := dib.GenerateDAG(buildPath1, registryPrefix, "", nil)
108107
require.NoError(t, err)
109108

110-
graph1, err := dib.GenerateDAG(fixtureDir,
111-
"eu.gcr.io/my-test-repository",
112-
"../../test/fixtures/dib/valid_wordlist.txt",
113-
map[string]string{})
109+
graph1, err := dib.GenerateDAG(buildPath1, registryPrefix,
110+
"../../test/fixtures/dib/valid_wordlist.txt", nil)
114111
require.NoError(t, err)
115112

116113
nodes0 := flattenNodes(graph0)
@@ -126,13 +123,10 @@ func TestGenerateDAG(t *testing.T) {
126123
})
127124

128125
t.Run("using arg used in root node should change all hashes", func(t *testing.T) {
129-
graph0, err := dib.GenerateDAG(fixtureDir,
130-
"eu.gcr.io/my-test-repository", "",
131-
map[string]string{})
126+
graph0, err := dib.GenerateDAG(buildPath1, registryPrefix, "", nil)
132127
require.NoError(t, err)
133128

134-
graph1, err := dib.GenerateDAG(fixtureDir,
135-
"eu.gcr.io/my-test-repository", "",
129+
graph1, err := dib.GenerateDAG(buildPath1, registryPrefix, "",
136130
map[string]string{
137131
"HELLO": "world",
138132
})
@@ -145,14 +139,24 @@ func TestGenerateDAG(t *testing.T) {
145139

146140
assert.NotEqual(t, rootNode1.Image.Hash, rootNode0.Image.Hash)
147141
})
142+
143+
t.Run("duplicates", func(t *testing.T) {
144+
graph, err := dib.GenerateDAG(buildPath2, registryPrefix, "", nil)
145+
require.Error(t, err)
146+
require.Nil(t, graph)
147+
require.EqualError(t, err,
148+
fmt.Sprintf(
149+
"duplicate image name \"%s/duplicate\" found while reading file \"%s/bullseye/duplicate2/Dockerfile\": previous file was \"%s/bullseye/duplicate1/Dockerfile\"", //nolint:lll
150+
registryPrefix, buildPath2, buildPath2))
151+
})
148152
}
149153

150-
// copyFixtures copies the directory fixtureDir into a temporary one to be free to edit files.
151-
func copyFixtures(t *testing.T) string {
154+
// copyFixtures copies the buildPath directory into a temporary one to be free to edit files.
155+
func copyFixtures(t *testing.T, buildPath string) string {
152156
t.Helper()
153157
cwd, err := os.Getwd()
154158
require.NoError(t, err)
155-
src := path.Join(cwd, fixtureDir)
159+
src := path.Join(cwd, buildPath)
156160
dest := t.TempDir()
157161
cmd := exec.Command("cp", "-r", src, dest)
158162
require.NoError(t, cmd.Run())
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM debian:bullseye
2+
3+
LABEL name="bullseye"
4+
LABEL version="v1"
5+
6+
ARG HELLO="there"
7+
8+
RUN echo "Hello $HELLO"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM eu.gcr.io/my-test-repository/bullseye:v1
2+
3+
LABEL name="duplicate"
4+
LABEL version="v1"
5+
LABEL dib.use-custom-hash-list="true"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM eu.gcr.io/my-test-repository/bullseye:v1
2+
3+
LABEL name="duplicate"
4+
LABEL version="v2"
5+
LABEL dib.use-custom-hash-list="true"

0 commit comments

Comments
 (0)