github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/builder/dockerfile/internals_test.go (about) 1 package dockerfile // import "github.com/docker/docker/builder/dockerfile" 2 3 import ( 4 "fmt" 5 "os" 6 "runtime" 7 "testing" 8 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/backend" 11 "github.com/docker/docker/api/types/container" 12 "github.com/docker/docker/builder" 13 "github.com/docker/docker/builder/remotecontext" 14 "github.com/docker/docker/image" 15 "github.com/docker/docker/layer" 16 "github.com/docker/docker/pkg/archive" 17 "github.com/docker/go-connections/nat" 18 "github.com/opencontainers/go-digest" 19 "gotest.tools/v3/assert" 20 is "gotest.tools/v3/assert/cmp" 21 "gotest.tools/v3/skip" 22 ) 23 24 func TestEmptyDockerfile(t *testing.T) { 25 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 26 defer cleanup() 27 28 createTestTempFile(t, contextDir, builder.DefaultDockerfileName, "", 0777) 29 30 readAndCheckDockerfile(t, "emptyDockerfile", contextDir, "", "the Dockerfile (Dockerfile) cannot be empty") 31 } 32 33 func TestSymlinkDockerfile(t *testing.T) { 34 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 35 defer cleanup() 36 37 createTestSymlink(t, contextDir, builder.DefaultDockerfileName, "/etc/passwd") 38 39 // The reason the error is "Cannot locate specified Dockerfile" is because 40 // in the builder, the symlink is resolved within the context, therefore 41 // Dockerfile -> /etc/passwd becomes etc/passwd from the context which is 42 // a nonexistent file. 43 expectedError := fmt.Sprintf("Cannot locate specified Dockerfile: %s", builder.DefaultDockerfileName) 44 45 readAndCheckDockerfile(t, "symlinkDockerfile", contextDir, builder.DefaultDockerfileName, expectedError) 46 } 47 48 func TestDockerfileOutsideTheBuildContext(t *testing.T) { 49 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 50 defer cleanup() 51 52 expectedError := "path outside the build context: ../../Dockerfile ()" 53 if runtime.GOOS == "windows" { 54 expectedError = "failed to resolve scoped path ../../Dockerfile ()" 55 } 56 57 readAndCheckDockerfile(t, "DockerfileOutsideTheBuildContext", contextDir, "../../Dockerfile", expectedError) 58 } 59 60 func TestNonExistingDockerfile(t *testing.T) { 61 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 62 defer cleanup() 63 64 expectedError := "Cannot locate specified Dockerfile: Dockerfile" 65 66 readAndCheckDockerfile(t, "NonExistingDockerfile", contextDir, "Dockerfile", expectedError) 67 } 68 69 func readAndCheckDockerfile(t *testing.T, testName, contextDir, dockerfilePath, expectedError string) { 70 if runtime.GOOS != "windows" { 71 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 72 } 73 tarStream, err := archive.Tar(contextDir, archive.Uncompressed) 74 assert.NilError(t, err) 75 76 defer func() { 77 if err = tarStream.Close(); err != nil { 78 t.Fatalf("Error when closing tar stream: %s", err) 79 } 80 }() 81 82 if dockerfilePath == "" { // handled in BuildWithContext 83 dockerfilePath = builder.DefaultDockerfileName 84 } 85 86 config := backend.BuildConfig{ 87 Options: &types.ImageBuildOptions{Dockerfile: dockerfilePath}, 88 Source: tarStream, 89 } 90 _, _, err = remotecontext.Detect(config) 91 assert.Check(t, is.ErrorContains(err, expectedError)) 92 } 93 94 func TestCopyRunConfig(t *testing.T) { 95 defaultEnv := []string{"foo=1"} 96 defaultCmd := []string{"old"} 97 98 var testcases = []struct { 99 doc string 100 modifiers []runConfigModifier 101 expected *container.Config 102 }{ 103 { 104 doc: "Set the command", 105 modifiers: []runConfigModifier{withCmd([]string{"new"})}, 106 expected: &container.Config{ 107 Cmd: []string{"new"}, 108 Env: defaultEnv, 109 }, 110 }, 111 { 112 doc: "Set the command to a comment", 113 modifiers: []runConfigModifier{withCmdComment("comment", runtime.GOOS)}, 114 expected: &container.Config{ 115 Cmd: append(defaultShellForOS(runtime.GOOS), "#(nop) ", "comment"), 116 Env: defaultEnv, 117 }, 118 }, 119 { 120 doc: "Set the command and env", 121 modifiers: []runConfigModifier{ 122 withCmd([]string{"new"}), 123 withEnv([]string{"one", "two"}), 124 }, 125 expected: &container.Config{ 126 Cmd: []string{"new"}, 127 Env: []string{"one", "two"}, 128 }, 129 }, 130 } 131 132 for _, testcase := range testcases { 133 runConfig := &container.Config{ 134 Cmd: defaultCmd, 135 Env: defaultEnv, 136 } 137 runConfigCopy := copyRunConfig(runConfig, testcase.modifiers...) 138 assert.Check(t, is.DeepEqual(testcase.expected, runConfigCopy), testcase.doc) 139 // Assert the original was not modified 140 assert.Check(t, runConfig != runConfigCopy, testcase.doc) 141 } 142 } 143 144 func fullMutableRunConfig() *container.Config { 145 return &container.Config{ 146 Cmd: []string{"command", "arg1"}, 147 Env: []string{"env1=foo", "env2=bar"}, 148 ExposedPorts: nat.PortSet{ 149 "1000/tcp": {}, 150 "1001/tcp": {}, 151 }, 152 Volumes: map[string]struct{}{ 153 "one": {}, 154 "two": {}, 155 }, 156 Entrypoint: []string{"entry", "arg1"}, 157 OnBuild: []string{"first", "next"}, 158 Labels: map[string]string{ 159 "label1": "value1", 160 "label2": "value2", 161 }, 162 Shell: []string{"shell", "-c"}, 163 } 164 } 165 166 func TestDeepCopyRunConfig(t *testing.T) { 167 runConfig := fullMutableRunConfig() 168 copy := copyRunConfig(runConfig) 169 assert.Check(t, is.DeepEqual(fullMutableRunConfig(), copy)) 170 171 copy.Cmd[1] = "arg2" 172 copy.Env[1] = "env2=new" 173 copy.ExposedPorts["10002"] = struct{}{} 174 copy.Volumes["three"] = struct{}{} 175 copy.Entrypoint[1] = "arg2" 176 copy.OnBuild[0] = "start" 177 copy.Labels["label3"] = "value3" 178 copy.Shell[0] = "sh" 179 assert.Check(t, is.DeepEqual(fullMutableRunConfig(), runConfig)) 180 } 181 182 type MockRWLayer struct{} 183 184 func (l *MockRWLayer) Release() error { return nil } 185 func (l *MockRWLayer) Root() string { return "" } 186 func (l *MockRWLayer) Commit() (builder.ROLayer, error) { 187 return &MockROLayer{ 188 diffID: layer.DiffID(digest.Digest("sha256:1234")), 189 }, nil 190 } 191 192 type MockROLayer struct { 193 diffID layer.DiffID 194 } 195 196 func (l *MockROLayer) Release() error { return nil } 197 func (l *MockROLayer) NewRWLayer() (builder.RWLayer, error) { return nil, nil } 198 func (l *MockROLayer) DiffID() layer.DiffID { return l.diffID } 199 200 func getMockBuildBackend() builder.Backend { 201 return &MockBackend{} 202 } 203 204 func TestExportImage(t *testing.T) { 205 ds := newDispatchState(NewBuildArgs(map[string]*string{})) 206 layer := &MockRWLayer{} 207 parentImage := &image.Image{ 208 V1Image: image.V1Image{ 209 OS: "linux", 210 Architecture: "arm64", 211 Variant: "v8", 212 }, 213 } 214 runConfig := &container.Config{} 215 216 b := &Builder{ 217 imageSources: getMockImageSource(nil, nil, nil), 218 docker: getMockBuildBackend(), 219 } 220 err := b.exportImage(ds, layer, parentImage, runConfig) 221 assert.NilError(t, err) 222 }