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