github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/builder/dockerfile/evaluator_test.go (about) 1 package dockerfile 2 3 import ( 4 "io/ioutil" 5 "strings" 6 "testing" 7 8 "github.com/docker/docker/api/types" 9 "github.com/docker/docker/api/types/container" 10 "github.com/docker/docker/builder" 11 "github.com/docker/docker/builder/dockerfile/parser" 12 "github.com/docker/docker/pkg/archive" 13 "github.com/docker/docker/pkg/reexec" 14 ) 15 16 type dispatchTestCase struct { 17 name, dockerfile, expectedError string 18 files map[string]string 19 } 20 21 func init() { 22 reexec.Init() 23 } 24 25 func initDispatchTestCases() []dispatchTestCase { 26 dispatchTestCases := []dispatchTestCase{{ 27 name: "copyEmptyWhitespace", 28 dockerfile: `COPY 29 quux \ 30 bar`, 31 expectedError: "COPY requires at least two arguments", 32 }, 33 { 34 name: "ONBUILD forbidden FROM", 35 dockerfile: "ONBUILD FROM scratch", 36 expectedError: "FROM isn't allowed as an ONBUILD trigger", 37 files: nil, 38 }, 39 { 40 name: "ONBUILD forbidden MAINTAINER", 41 dockerfile: "ONBUILD MAINTAINER docker.io", 42 expectedError: "MAINTAINER isn't allowed as an ONBUILD trigger", 43 files: nil, 44 }, 45 { 46 name: "ARG two arguments", 47 dockerfile: "ARG foo bar", 48 expectedError: "ARG requires exactly one argument", 49 files: nil, 50 }, 51 { 52 name: "MAINTAINER unknown flag", 53 dockerfile: "MAINTAINER --boo joe@example.com", 54 expectedError: "Unknown flag: boo", 55 files: nil, 56 }, 57 { 58 name: "ADD multiple files to file", 59 dockerfile: "ADD file1.txt file2.txt test", 60 expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /", 61 files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"}, 62 }, 63 { 64 name: "JSON ADD multiple files to file", 65 dockerfile: `ADD ["file1.txt", "file2.txt", "test"]`, 66 expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /", 67 files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"}, 68 }, 69 { 70 name: "Wildcard ADD multiple files to file", 71 dockerfile: "ADD file*.txt test", 72 expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /", 73 files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"}, 74 }, 75 { 76 name: "Wildcard JSON ADD multiple files to file", 77 dockerfile: `ADD ["file*.txt", "test"]`, 78 expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /", 79 files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"}, 80 }, 81 { 82 name: "COPY multiple files to file", 83 dockerfile: "COPY file1.txt file2.txt test", 84 expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /", 85 files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"}, 86 }, 87 { 88 name: "JSON COPY multiple files to file", 89 dockerfile: `COPY ["file1.txt", "file2.txt", "test"]`, 90 expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /", 91 files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"}, 92 }, 93 { 94 name: "ADD multiple files to file with whitespace", 95 dockerfile: `ADD [ "test file1.txt", "test file2.txt", "test" ]`, 96 expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /", 97 files: map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"}, 98 }, 99 { 100 name: "COPY multiple files to file with whitespace", 101 dockerfile: `COPY [ "test file1.txt", "test file2.txt", "test" ]`, 102 expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /", 103 files: map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"}, 104 }, 105 { 106 name: "COPY wildcard no files", 107 dockerfile: `COPY file*.txt /tmp/`, 108 expectedError: "No source files were specified", 109 files: nil, 110 }, 111 { 112 name: "COPY url", 113 dockerfile: `COPY https://index.docker.io/robots.txt /`, 114 expectedError: "Source can't be a URL for COPY", 115 files: nil, 116 }, 117 { 118 name: "Chaining ONBUILD", 119 dockerfile: `ONBUILD ONBUILD RUN touch foobar`, 120 expectedError: "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed", 121 files: nil, 122 }, 123 { 124 name: "Invalid instruction", 125 dockerfile: `foo bar`, 126 expectedError: "Unknown instruction: FOO", 127 files: nil, 128 }} 129 130 return dispatchTestCases 131 } 132 133 func TestDispatch(t *testing.T) { 134 testCases := initDispatchTestCases() 135 136 for _, testCase := range testCases { 137 executeTestCase(t, testCase) 138 } 139 } 140 141 func executeTestCase(t *testing.T, testCase dispatchTestCase) { 142 contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") 143 defer cleanup() 144 145 for filename, content := range testCase.files { 146 createTestTempFile(t, contextDir, filename, content, 0777) 147 } 148 149 tarStream, err := archive.Tar(contextDir, archive.Uncompressed) 150 151 if err != nil { 152 t.Fatalf("Error when creating tar stream: %s", err) 153 } 154 155 defer func() { 156 if err = tarStream.Close(); err != nil { 157 t.Fatalf("Error when closing tar stream: %s", err) 158 } 159 }() 160 161 context, err := builder.MakeTarSumContext(tarStream) 162 163 if err != nil { 164 t.Fatalf("Error when creating tar context: %s", err) 165 } 166 167 defer func() { 168 if err = context.Close(); err != nil { 169 t.Fatalf("Error when closing tar context: %s", err) 170 } 171 }() 172 173 r := strings.NewReader(testCase.dockerfile) 174 d := parser.Directive{} 175 parser.SetEscapeToken(parser.DefaultEscapeToken, &d) 176 n, err := parser.Parse(r, &d) 177 178 if err != nil { 179 t.Fatalf("Error when parsing Dockerfile: %s", err) 180 } 181 182 config := &container.Config{} 183 options := &types.ImageBuildOptions{} 184 185 b := &Builder{runConfig: config, options: options, Stdout: ioutil.Discard, context: context} 186 187 err = b.dispatch(0, len(n.Children), n.Children[0]) 188 189 if err == nil { 190 t.Fatalf("No error when executing test %s", testCase.name) 191 } 192 193 if !strings.Contains(err.Error(), testCase.expectedError) { 194 t.Fatalf("Wrong error message. Should be \"%s\". Got \"%s\"", testCase.expectedError, err.Error()) 195 } 196 197 }