github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/integration/build/build_test.go (about) 1 package build // import "github.com/docker/docker/integration/build" 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "context" 7 "encoding/json" 8 "io" 9 "io/ioutil" 10 "strings" 11 "testing" 12 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/api/types/filters" 15 "github.com/docker/docker/api/types/versions" 16 "github.com/docker/docker/integration/internal/request" 17 "github.com/docker/docker/internal/test/fakecontext" 18 "github.com/docker/docker/pkg/jsonmessage" 19 "github.com/gotestyourself/gotestyourself/assert" 20 is "github.com/gotestyourself/gotestyourself/assert/cmp" 21 "github.com/gotestyourself/gotestyourself/skip" 22 ) 23 24 func TestBuildWithRemoveAndForceRemove(t *testing.T) { 25 defer setupTest(t)() 26 t.Parallel() 27 cases := []struct { 28 name string 29 dockerfile string 30 numberOfIntermediateContainers int 31 rm bool 32 forceRm bool 33 }{ 34 { 35 name: "successful build with no removal", 36 dockerfile: `FROM busybox 37 RUN exit 0 38 RUN exit 0`, 39 numberOfIntermediateContainers: 2, 40 rm: false, 41 forceRm: false, 42 }, 43 { 44 name: "successful build with remove", 45 dockerfile: `FROM busybox 46 RUN exit 0 47 RUN exit 0`, 48 numberOfIntermediateContainers: 0, 49 rm: true, 50 forceRm: false, 51 }, 52 { 53 name: "successful build with remove and force remove", 54 dockerfile: `FROM busybox 55 RUN exit 0 56 RUN exit 0`, 57 numberOfIntermediateContainers: 0, 58 rm: true, 59 forceRm: true, 60 }, 61 { 62 name: "failed build with no removal", 63 dockerfile: `FROM busybox 64 RUN exit 0 65 RUN exit 1`, 66 numberOfIntermediateContainers: 2, 67 rm: false, 68 forceRm: false, 69 }, 70 { 71 name: "failed build with remove", 72 dockerfile: `FROM busybox 73 RUN exit 0 74 RUN exit 1`, 75 numberOfIntermediateContainers: 1, 76 rm: true, 77 forceRm: false, 78 }, 79 { 80 name: "failed build with remove and force remove", 81 dockerfile: `FROM busybox 82 RUN exit 0 83 RUN exit 1`, 84 numberOfIntermediateContainers: 0, 85 rm: true, 86 forceRm: true, 87 }, 88 } 89 90 client := request.NewAPIClient(t) 91 ctx := context.Background() 92 for _, c := range cases { 93 t.Run(c.name, func(t *testing.T) { 94 t.Parallel() 95 dockerfile := []byte(c.dockerfile) 96 97 buff := bytes.NewBuffer(nil) 98 tw := tar.NewWriter(buff) 99 assert.NilError(t, tw.WriteHeader(&tar.Header{ 100 Name: "Dockerfile", 101 Size: int64(len(dockerfile)), 102 })) 103 _, err := tw.Write(dockerfile) 104 assert.NilError(t, err) 105 assert.NilError(t, tw.Close()) 106 resp, err := client.ImageBuild(ctx, buff, types.ImageBuildOptions{Remove: c.rm, ForceRemove: c.forceRm, NoCache: true}) 107 assert.NilError(t, err) 108 defer resp.Body.Close() 109 filter, err := buildContainerIdsFilter(resp.Body) 110 assert.NilError(t, err) 111 remainingContainers, err := client.ContainerList(ctx, types.ContainerListOptions{Filters: filter, All: true}) 112 assert.NilError(t, err) 113 assert.Equal(t, c.numberOfIntermediateContainers, len(remainingContainers), "Expected %v remaining intermediate containers, got %v", c.numberOfIntermediateContainers, len(remainingContainers)) 114 }) 115 } 116 } 117 118 func buildContainerIdsFilter(buildOutput io.Reader) (filters.Args, error) { 119 const intermediateContainerPrefix = " ---> Running in " 120 filter := filters.NewArgs() 121 122 dec := json.NewDecoder(buildOutput) 123 for { 124 m := jsonmessage.JSONMessage{} 125 err := dec.Decode(&m) 126 if err == io.EOF { 127 return filter, nil 128 } 129 if err != nil { 130 return filter, err 131 } 132 if ix := strings.Index(m.Stream, intermediateContainerPrefix); ix != -1 { 133 filter.Add("id", strings.TrimSpace(m.Stream[ix+len(intermediateContainerPrefix):])) 134 } 135 } 136 } 137 138 func TestBuildMultiStageParentConfig(t *testing.T) { 139 dockerfile := ` 140 FROM busybox AS stage0 141 ENV WHO=parent 142 WORKDIR /foo 143 144 FROM stage0 145 ENV WHO=sibling1 146 WORKDIR sub1 147 148 FROM stage0 149 WORKDIR sub2 150 ` 151 ctx := context.Background() 152 source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile)) 153 defer source.Close() 154 155 apiclient := testEnv.APIClient() 156 resp, err := apiclient.ImageBuild(ctx, 157 source.AsTarReader(t), 158 types.ImageBuildOptions{ 159 Remove: true, 160 ForceRemove: true, 161 Tags: []string{"build1"}, 162 }) 163 assert.NilError(t, err) 164 _, err = io.Copy(ioutil.Discard, resp.Body) 165 resp.Body.Close() 166 assert.NilError(t, err) 167 168 image, _, err := apiclient.ImageInspectWithRaw(ctx, "build1") 169 assert.NilError(t, err) 170 171 assert.Check(t, is.Equal("/foo/sub2", image.Config.WorkingDir)) 172 assert.Check(t, is.Contains(image.Config.Env, "WHO=parent")) 173 } 174 175 func TestBuildWithEmptyLayers(t *testing.T) { 176 dockerfile := ` 177 FROM busybox 178 COPY 1/ /target/ 179 COPY 2/ /target/ 180 COPY 3/ /target/ 181 ` 182 ctx := context.Background() 183 source := fakecontext.New(t, "", 184 fakecontext.WithDockerfile(dockerfile), 185 fakecontext.WithFile("1/a", "asdf"), 186 fakecontext.WithFile("2/a", "asdf"), 187 fakecontext.WithFile("3/a", "asdf")) 188 defer source.Close() 189 190 apiclient := testEnv.APIClient() 191 resp, err := apiclient.ImageBuild(ctx, 192 source.AsTarReader(t), 193 types.ImageBuildOptions{ 194 Remove: true, 195 ForceRemove: true, 196 }) 197 assert.NilError(t, err) 198 _, err = io.Copy(ioutil.Discard, resp.Body) 199 resp.Body.Close() 200 assert.NilError(t, err) 201 } 202 203 // TestBuildMultiStageOnBuild checks that ONBUILD commands are applied to 204 // multiple subsequent stages 205 // #35652 206 func TestBuildMultiStageOnBuild(t *testing.T) { 207 defer setupTest(t)() 208 // test both metadata and layer based commands as they may be implemented differently 209 dockerfile := `FROM busybox AS stage1 210 ONBUILD RUN echo 'foo' >somefile 211 ONBUILD ENV bar=baz 212 213 FROM stage1 214 RUN cat somefile # fails if ONBUILD RUN fails 215 216 FROM stage1 217 RUN cat somefile` 218 219 ctx := context.Background() 220 source := fakecontext.New(t, "", 221 fakecontext.WithDockerfile(dockerfile)) 222 defer source.Close() 223 224 apiclient := testEnv.APIClient() 225 resp, err := apiclient.ImageBuild(ctx, 226 source.AsTarReader(t), 227 types.ImageBuildOptions{ 228 Remove: true, 229 ForceRemove: true, 230 }) 231 232 out := bytes.NewBuffer(nil) 233 assert.NilError(t, err) 234 _, err = io.Copy(out, resp.Body) 235 resp.Body.Close() 236 assert.NilError(t, err) 237 238 assert.Check(t, is.Contains(out.String(), "Successfully built")) 239 240 imageIDs, err := getImageIDsFromBuild(out.Bytes()) 241 assert.NilError(t, err) 242 assert.Check(t, is.Equal(3, len(imageIDs))) 243 244 image, _, err := apiclient.ImageInspectWithRaw(context.Background(), imageIDs[2]) 245 assert.NilError(t, err) 246 assert.Check(t, is.Contains(image.Config.Env, "bar=baz")) 247 } 248 249 // #35403 #36122 250 func TestBuildUncleanTarFilenames(t *testing.T) { 251 skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.37"), "broken in earlier versions") 252 ctx := context.TODO() 253 defer setupTest(t)() 254 255 dockerfile := `FROM scratch 256 COPY foo / 257 FROM scratch 258 COPY bar /` 259 260 buf := bytes.NewBuffer(nil) 261 w := tar.NewWriter(buf) 262 writeTarRecord(t, w, "Dockerfile", dockerfile) 263 writeTarRecord(t, w, "../foo", "foocontents0") 264 writeTarRecord(t, w, "/bar", "barcontents0") 265 err := w.Close() 266 assert.NilError(t, err) 267 268 apiclient := testEnv.APIClient() 269 resp, err := apiclient.ImageBuild(ctx, 270 buf, 271 types.ImageBuildOptions{ 272 Remove: true, 273 ForceRemove: true, 274 }) 275 276 out := bytes.NewBuffer(nil) 277 assert.NilError(t, err) 278 _, err = io.Copy(out, resp.Body) 279 resp.Body.Close() 280 assert.NilError(t, err) 281 282 // repeat with changed data should not cause cache hits 283 284 buf = bytes.NewBuffer(nil) 285 w = tar.NewWriter(buf) 286 writeTarRecord(t, w, "Dockerfile", dockerfile) 287 writeTarRecord(t, w, "../foo", "foocontents1") 288 writeTarRecord(t, w, "/bar", "barcontents1") 289 err = w.Close() 290 assert.NilError(t, err) 291 292 resp, err = apiclient.ImageBuild(ctx, 293 buf, 294 types.ImageBuildOptions{ 295 Remove: true, 296 ForceRemove: true, 297 }) 298 299 out = bytes.NewBuffer(nil) 300 assert.NilError(t, err) 301 _, err = io.Copy(out, resp.Body) 302 resp.Body.Close() 303 assert.NilError(t, err) 304 assert.Assert(t, !strings.Contains(out.String(), "Using cache")) 305 } 306 307 // docker/for-linux#135 308 // #35641 309 func TestBuildMultiStageLayerLeak(t *testing.T) { 310 skip.IfCondition(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.37"), "broken in earlier versions") 311 ctx := context.TODO() 312 defer setupTest(t)() 313 314 // all commands need to match until COPY 315 dockerfile := `FROM busybox 316 WORKDIR /foo 317 COPY foo . 318 FROM busybox 319 WORKDIR /foo 320 COPY bar . 321 RUN [ -f bar ] 322 RUN [ ! -f foo ] 323 ` 324 325 source := fakecontext.New(t, "", 326 fakecontext.WithFile("foo", "0"), 327 fakecontext.WithFile("bar", "1"), 328 fakecontext.WithDockerfile(dockerfile)) 329 defer source.Close() 330 331 apiclient := testEnv.APIClient() 332 resp, err := apiclient.ImageBuild(ctx, 333 source.AsTarReader(t), 334 types.ImageBuildOptions{ 335 Remove: true, 336 ForceRemove: true, 337 }) 338 339 out := bytes.NewBuffer(nil) 340 assert.NilError(t, err) 341 _, err = io.Copy(out, resp.Body) 342 resp.Body.Close() 343 assert.NilError(t, err) 344 345 assert.Check(t, is.Contains(out.String(), "Successfully built")) 346 } 347 348 func writeTarRecord(t *testing.T, w *tar.Writer, fn, contents string) { 349 err := w.WriteHeader(&tar.Header{ 350 Name: fn, 351 Mode: 0600, 352 Size: int64(len(contents)), 353 Typeflag: '0', 354 }) 355 assert.NilError(t, err) 356 _, err = w.Write([]byte(contents)) 357 assert.NilError(t, err) 358 } 359 360 type buildLine struct { 361 Stream string 362 Aux struct { 363 ID string 364 } 365 } 366 367 func getImageIDsFromBuild(output []byte) ([]string, error) { 368 ids := []string{} 369 for _, line := range bytes.Split(output, []byte("\n")) { 370 if len(line) == 0 { 371 continue 372 } 373 entry := buildLine{} 374 if err := json.Unmarshal(line, &entry); err != nil { 375 return nil, err 376 } 377 if entry.Aux.ID != "" { 378 ids = append(ids, entry.Aux.ID) 379 } 380 } 381 return ids, nil 382 }