2018-02-05 16:05:59 -05:00
package build // import "github.com/docker/docker/integration/build"
2017-10-09 09:17:24 -04:00
import (
"archive/tar"
"bytes"
"context"
"encoding/json"
"io"
2021-08-24 06:10:50 -04:00
"os"
2017-10-09 09:17:24 -04:00
"strings"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
2018-03-14 06:21:21 -04:00
"github.com/docker/docker/api/types/versions"
2019-07-15 12:19:31 -04:00
"github.com/docker/docker/errdefs"
2017-10-09 09:17:24 -04:00
"github.com/docker/docker/pkg/jsonmessage"
2019-08-29 16:52:40 -04:00
"github.com/docker/docker/testutil/fakecontext"
2020-02-07 08:39:24 -05:00
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
2017-10-09 09:17:24 -04:00
)
func TestBuildWithRemoveAndForceRemove ( t * testing . T ) {
2017-10-18 17:59:16 -04:00
defer setupTest ( t ) ( )
2019-01-03 05:49:00 -05:00
2017-10-09 09:17:24 -04:00
cases := [ ] struct {
name string
dockerfile string
numberOfIntermediateContainers int
rm bool
forceRm bool
} {
{
name : "successful build with no removal" ,
dockerfile : ` FROM busybox
RUN exit 0
RUN exit 0 ` ,
numberOfIntermediateContainers : 2 ,
2020-02-11 12:53:01 -05:00
rm : false ,
forceRm : false ,
2017-10-09 09:17:24 -04:00
} ,
{
name : "successful build with remove" ,
dockerfile : ` FROM busybox
RUN exit 0
RUN exit 0 ` ,
numberOfIntermediateContainers : 0 ,
2020-02-11 12:53:01 -05:00
rm : true ,
forceRm : false ,
2017-10-09 09:17:24 -04:00
} ,
{
name : "successful build with remove and force remove" ,
dockerfile : ` FROM busybox
RUN exit 0
RUN exit 0 ` ,
numberOfIntermediateContainers : 0 ,
2020-02-11 12:53:01 -05:00
rm : true ,
forceRm : true ,
2017-10-09 09:17:24 -04:00
} ,
{
name : "failed build with no removal" ,
dockerfile : ` FROM busybox
RUN exit 0
RUN exit 1 ` ,
numberOfIntermediateContainers : 2 ,
2020-02-11 12:53:01 -05:00
rm : false ,
forceRm : false ,
2017-10-09 09:17:24 -04:00
} ,
{
name : "failed build with remove" ,
dockerfile : ` FROM busybox
RUN exit 0
RUN exit 1 ` ,
numberOfIntermediateContainers : 1 ,
2020-02-11 12:53:01 -05:00
rm : true ,
forceRm : false ,
2017-10-09 09:17:24 -04:00
} ,
{
name : "failed build with remove and force remove" ,
dockerfile : ` FROM busybox
RUN exit 0
RUN exit 1 ` ,
numberOfIntermediateContainers : 0 ,
2020-02-11 12:53:01 -05:00
rm : true ,
forceRm : true ,
2017-10-09 09:17:24 -04:00
} ,
}
2019-01-02 08:16:25 -05:00
client := testEnv . APIClient ( )
2017-10-09 09:17:24 -04:00
ctx := context . Background ( )
for _ , c := range cases {
t . Run ( c . name , func ( t * testing . T ) {
t . Parallel ( )
dockerfile := [ ] byte ( c . dockerfile )
buff := bytes . NewBuffer ( nil )
tw := tar . NewWriter ( buff )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , tw . WriteHeader ( & tar . Header {
2017-10-09 09:17:24 -04:00
Name : "Dockerfile" ,
Size : int64 ( len ( dockerfile ) ) ,
} ) )
_ , err := tw . Write ( dockerfile )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
assert . NilError ( t , tw . Close ( ) )
2017-10-09 09:17:24 -04:00
resp , err := client . ImageBuild ( ctx , buff , types . ImageBuildOptions { Remove : c . rm , ForceRemove : c . forceRm , NoCache : true } )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2017-10-09 09:17:24 -04:00
defer resp . Body . Close ( )
filter , err := buildContainerIdsFilter ( resp . Body )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2017-10-09 09:17:24 -04:00
remainingContainers , err := client . ContainerList ( ctx , types . ContainerListOptions { Filters : filter , All : true } )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
assert . Equal ( t , c . numberOfIntermediateContainers , len ( remainingContainers ) , "Expected %v remaining intermediate containers, got %v" , c . numberOfIntermediateContainers , len ( remainingContainers ) )
2017-10-09 09:17:24 -04:00
} )
}
}
func buildContainerIdsFilter ( buildOutput io . Reader ) ( filters . Args , error ) {
const intermediateContainerPrefix = " ---> Running in "
filter := filters . NewArgs ( )
dec := json . NewDecoder ( buildOutput )
for {
m := jsonmessage . JSONMessage { }
err := dec . Decode ( & m )
if err == io . EOF {
return filter , nil
}
if err != nil {
return filter , err
}
if ix := strings . Index ( m . Stream , intermediateContainerPrefix ) ; ix != - 1 {
filter . Add ( "id" , strings . TrimSpace ( m . Stream [ ix + len ( intermediateContainerPrefix ) : ] ) )
}
}
}
2017-11-09 16:20:51 -05:00
2019-08-08 04:55:12 -04:00
// TestBuildMultiStageCopy verifies that copying between stages works correctly.
//
// Regression test for docker/for-win#4349, ENGCORE-935, where creating the target
// directory failed on Windows, because `os.MkdirAll()` was called with a volume
// GUID path (\\?\Volume{dae8d3ac-b9a1-11e9-88eb-e8554b2ba1db}\newdir\hello}),
// which currently isn't supported by Golang.
func TestBuildMultiStageCopy ( t * testing . T ) {
ctx := context . Background ( )
2021-08-24 06:10:50 -04:00
dockerfile , err := os . ReadFile ( "testdata/Dockerfile." + t . Name ( ) )
2019-08-08 04:55:12 -04:00
assert . NilError ( t , err )
source := fakecontext . New ( t , "" , fakecontext . WithDockerfile ( string ( dockerfile ) ) )
defer source . Close ( )
apiclient := testEnv . APIClient ( )
for _ , target := range [ ] string { "copy_to_root" , "copy_to_newdir" , "copy_to_newdir_nested" , "copy_to_existingdir" , "copy_to_newsubdir" } {
t . Run ( target , func ( t * testing . T ) {
imgName := strings . ToLower ( t . Name ( ) )
resp , err := apiclient . ImageBuild (
ctx ,
source . AsTarReader ( t ) ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
Target : target ,
Tags : [ ] string { imgName } ,
} ,
)
assert . NilError ( t , err )
out := bytes . NewBuffer ( nil )
_ , err = io . Copy ( out , resp . Body )
_ = resp . Body . Close ( )
if err != nil {
t . Log ( out )
}
assert . NilError ( t , err )
// verify the image was successfully built
_ , _ , err = apiclient . ImageInspectWithRaw ( ctx , imgName )
if err != nil {
t . Log ( out )
}
assert . NilError ( t , err )
} )
}
}
2017-11-09 16:20:51 -05:00
func TestBuildMultiStageParentConfig ( t * testing . T ) {
2018-05-04 17:15:00 -04:00
skip . If ( t , versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.35" ) , "broken in earlier versions" )
2017-11-09 16:20:51 -05:00
dockerfile := `
FROM busybox AS stage0
ENV WHO = parent
WORKDIR / foo
FROM stage0
ENV WHO = sibling1
WORKDIR sub1
FROM stage0
WORKDIR sub2
`
ctx := context . Background ( )
source := fakecontext . New ( t , "" , fakecontext . WithDockerfile ( dockerfile ) )
defer source . Close ( )
apiclient := testEnv . APIClient ( )
2020-05-18 08:42:29 -04:00
imgName := strings . ToLower ( t . Name ( ) )
2017-11-09 16:20:51 -05:00
resp , err := apiclient . ImageBuild ( ctx ,
source . AsTarReader ( t ) ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
2020-05-18 08:42:29 -04:00
Tags : [ ] string { imgName } ,
2017-11-09 16:20:51 -05:00
} )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2021-08-24 06:10:50 -04:00
_ , err = io . Copy ( io . Discard , resp . Body )
2017-11-09 16:20:51 -05:00
resp . Body . Close ( )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2017-11-09 16:20:51 -05:00
2020-05-18 08:42:29 -04:00
image , _ , err := apiclient . ImageInspectWithRaw ( ctx , imgName )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2017-11-09 16:20:51 -05:00
2018-04-19 05:14:15 -04:00
expected := "/foo/sub2"
if testEnv . DaemonInfo . OSType == "windows" {
expected = ` C:\foo\sub2 `
}
assert . Check ( t , is . Equal ( expected , image . Config . WorkingDir ) )
2018-03-13 15:28:34 -04:00
assert . Check ( t , is . Contains ( image . Config . Env , "WHO=parent" ) )
2017-11-09 16:20:51 -05:00
}
2017-11-22 12:55:36 -05:00
2018-05-07 03:06:07 -04:00
// Test cases in #36996
func TestBuildLabelWithTargets ( t * testing . T ) {
2018-05-23 20:39:56 -04:00
skip . If ( t , versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.38" ) , "test added after 1.38" )
2018-08-02 16:24:51 -04:00
skip . If ( t , testEnv . DaemonInfo . OSType == "windows" , "FIXME" )
2020-05-18 08:42:29 -04:00
imgName := strings . ToLower ( t . Name ( ) + "-a" )
2018-05-07 03:06:07 -04:00
testLabels := map [ string ] string {
"foo" : "bar" ,
"dead" : "beef" ,
}
dockerfile := `
FROM busybox AS target - a
CMD [ "/dev" ]
LABEL label - a = inline - a
FROM busybox AS target - b
CMD [ "/dist" ]
LABEL label - b = inline - b
`
ctx := context . Background ( )
source := fakecontext . New ( t , "" , fakecontext . WithDockerfile ( dockerfile ) )
defer source . Close ( )
apiclient := testEnv . APIClient ( )
// For `target-a` build
resp , err := apiclient . ImageBuild ( ctx ,
source . AsTarReader ( t ) ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
2020-05-18 08:42:29 -04:00
Tags : [ ] string { imgName } ,
2018-05-07 03:06:07 -04:00
Labels : testLabels ,
Target : "target-a" ,
} )
assert . NilError ( t , err )
2021-08-24 06:10:50 -04:00
_ , err = io . Copy ( io . Discard , resp . Body )
2018-05-07 03:06:07 -04:00
resp . Body . Close ( )
assert . NilError ( t , err )
2020-05-18 08:42:29 -04:00
image , _ , err := apiclient . ImageInspectWithRaw ( ctx , imgName )
2018-05-07 03:06:07 -04:00
assert . NilError ( t , err )
testLabels [ "label-a" ] = "inline-a"
for k , v := range testLabels {
x , ok := image . Config . Labels [ k ]
assert . Assert ( t , ok )
assert . Assert ( t , x == v )
}
// For `target-b` build
2020-05-18 08:42:29 -04:00
imgName = strings . ToLower ( t . Name ( ) + "-b" )
2018-05-07 03:06:07 -04:00
delete ( testLabels , "label-a" )
resp , err = apiclient . ImageBuild ( ctx ,
source . AsTarReader ( t ) ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
2020-05-18 08:42:29 -04:00
Tags : [ ] string { imgName } ,
2018-05-07 03:06:07 -04:00
Labels : testLabels ,
Target : "target-b" ,
} )
assert . NilError ( t , err )
2021-08-24 06:10:50 -04:00
_ , err = io . Copy ( io . Discard , resp . Body )
2018-05-07 03:06:07 -04:00
resp . Body . Close ( )
assert . NilError ( t , err )
2020-05-18 08:42:29 -04:00
image , _ , err = apiclient . ImageInspectWithRaw ( ctx , imgName )
2018-05-07 03:06:07 -04:00
assert . NilError ( t , err )
testLabels [ "label-b" ] = "inline-b"
for k , v := range testLabels {
x , ok := image . Config . Labels [ k ]
assert . Assert ( t , ok )
assert . Assert ( t , x == v )
}
}
2017-11-22 12:55:36 -05:00
func TestBuildWithEmptyLayers ( t * testing . T ) {
dockerfile := `
FROM busybox
COPY 1 / / target /
COPY 2 / / target /
COPY 3 / / target /
`
ctx := context . Background ( )
source := fakecontext . New ( t , "" ,
fakecontext . WithDockerfile ( dockerfile ) ,
fakecontext . WithFile ( "1/a" , "asdf" ) ,
fakecontext . WithFile ( "2/a" , "asdf" ) ,
fakecontext . WithFile ( "3/a" , "asdf" ) )
defer source . Close ( )
apiclient := testEnv . APIClient ( )
resp , err := apiclient . ImageBuild ( ctx ,
source . AsTarReader ( t ) ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
} )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2021-08-24 06:10:50 -04:00
_ , err = io . Copy ( io . Discard , resp . Body )
2017-11-22 12:55:36 -05:00
resp . Body . Close ( )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2017-11-22 12:55:36 -05:00
}
2017-12-01 13:31:03 -05:00
// TestBuildMultiStageOnBuild checks that ONBUILD commands are applied to
// multiple subsequent stages
// #35652
func TestBuildMultiStageOnBuild ( t * testing . T ) {
2018-05-04 17:15:00 -04:00
skip . If ( t , versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.33" ) , "broken in earlier versions" )
2017-12-01 13:31:03 -05:00
defer setupTest ( t ) ( )
// test both metadata and layer based commands as they may be implemented differently
dockerfile := ` FROM busybox AS stage1
ONBUILD RUN echo ' foo ' > somefile
ONBUILD ENV bar = baz
FROM stage1
2018-04-19 05:14:15 -04:00
# fails if ONBUILD RUN fails
RUN cat somefile
2017-12-01 13:31:03 -05:00
FROM stage1
RUN cat somefile `
ctx := context . Background ( )
source := fakecontext . New ( t , "" ,
fakecontext . WithDockerfile ( dockerfile ) )
defer source . Close ( )
apiclient := testEnv . APIClient ( )
resp , err := apiclient . ImageBuild ( ctx ,
source . AsTarReader ( t ) ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
} )
out := bytes . NewBuffer ( nil )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2017-12-01 13:31:03 -05:00
_ , err = io . Copy ( out , resp . Body )
resp . Body . Close ( )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2017-12-01 13:31:03 -05:00
2018-03-13 15:28:34 -04:00
assert . Check ( t , is . Contains ( out . String ( ) , "Successfully built" ) )
2017-12-01 13:31:03 -05:00
imageIDs , err := getImageIDsFromBuild ( out . Bytes ( ) )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2020-06-04 14:18:18 -04:00
assert . Assert ( t , is . Equal ( 3 , len ( imageIDs ) ) )
2017-12-01 13:31:03 -05:00
image , _ , err := apiclient . ImageInspectWithRaw ( context . Background ( ) , imageIDs [ 2 ] )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
assert . Check ( t , is . Contains ( image . Config . Env , "bar=baz" ) )
2017-12-01 13:31:03 -05:00
}
2018-02-15 21:00:39 -05:00
// #35403 #36122
func TestBuildUncleanTarFilenames ( t * testing . T ) {
2018-03-27 12:13:47 -04:00
skip . If ( t , versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.37" ) , "broken in earlier versions" )
2018-04-19 05:14:15 -04:00
skip . If ( t , testEnv . DaemonInfo . OSType == "windows" , "FIXME" )
2018-02-15 21:00:39 -05:00
ctx := context . TODO ( )
defer setupTest ( t ) ( )
dockerfile := ` FROM scratch
COPY foo /
FROM scratch
COPY bar / `
buf := bytes . NewBuffer ( nil )
w := tar . NewWriter ( buf )
writeTarRecord ( t , w , "Dockerfile" , dockerfile )
writeTarRecord ( t , w , "../foo" , "foocontents0" )
writeTarRecord ( t , w , "/bar" , "barcontents0" )
err := w . Close ( )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2018-02-15 21:00:39 -05:00
apiclient := testEnv . APIClient ( )
resp , err := apiclient . ImageBuild ( ctx ,
buf ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
} )
out := bytes . NewBuffer ( nil )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2018-02-15 21:00:39 -05:00
_ , err = io . Copy ( out , resp . Body )
resp . Body . Close ( )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2018-02-15 21:00:39 -05:00
// repeat with changed data should not cause cache hits
buf = bytes . NewBuffer ( nil )
w = tar . NewWriter ( buf )
writeTarRecord ( t , w , "Dockerfile" , dockerfile )
writeTarRecord ( t , w , "../foo" , "foocontents1" )
writeTarRecord ( t , w , "/bar" , "barcontents1" )
err = w . Close ( )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2018-02-15 21:00:39 -05:00
resp , err = apiclient . ImageBuild ( ctx ,
buf ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
} )
out = bytes . NewBuffer ( nil )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2018-02-15 21:00:39 -05:00
_ , err = io . Copy ( out , resp . Body )
resp . Body . Close ( )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2017-12-22 16:30:49 -05:00
assert . Assert ( t , ! strings . Contains ( out . String ( ) , "Using cache" ) )
2018-02-15 21:00:39 -05:00
}
2018-02-16 16:50:57 -05:00
// docker/for-linux#135
// #35641
func TestBuildMultiStageLayerLeak ( t * testing . T ) {
2018-06-11 09:32:11 -04:00
skip . If ( t , versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.37" ) , "broken in earlier versions" )
2018-02-16 16:50:57 -05:00
ctx := context . TODO ( )
defer setupTest ( t ) ( )
// all commands need to match until COPY
dockerfile := ` FROM busybox
WORKDIR / foo
COPY foo .
FROM busybox
WORKDIR / foo
COPY bar .
RUN [ - f bar ]
RUN [ ! - f foo ]
`
source := fakecontext . New ( t , "" ,
fakecontext . WithFile ( "foo" , "0" ) ,
fakecontext . WithFile ( "bar" , "1" ) ,
fakecontext . WithDockerfile ( dockerfile ) )
defer source . Close ( )
apiclient := testEnv . APIClient ( )
resp , err := apiclient . ImageBuild ( ctx ,
source . AsTarReader ( t ) ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
} )
out := bytes . NewBuffer ( nil )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2018-02-16 16:50:57 -05:00
_ , err = io . Copy ( out , resp . Body )
resp . Body . Close ( )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2018-02-16 16:50:57 -05:00
2018-03-13 15:28:34 -04:00
assert . Check ( t , is . Contains ( out . String ( ) , "Successfully built" ) )
2018-02-16 16:50:57 -05:00
}
2018-09-05 22:11:04 -04:00
// #37581
2020-09-09 10:50:59 -04:00
// #40444 (Windows Containers only)
2018-09-05 22:11:04 -04:00
func TestBuildWithHugeFile ( t * testing . T ) {
ctx := context . TODO ( )
defer setupTest ( t ) ( )
dockerfile := ` FROM busybox
2020-09-09 10:50:59 -04:00
`
if testEnv . DaemonInfo . OSType == "windows" {
dockerfile += ` # create a file with size of 8 GB
RUN powershell "fsutil.exe file createnew bigfile.txt 8589934592 ; dir bigfile.txt" `
} else {
dockerfile += ` # create a sparse file with size over 8 GB
2018-09-05 22:11:04 -04:00
RUN for g in $ ( seq 0 8 ) ; do dd if = / dev / urandom of = rnd bs = 1 K count = 1 seek = $ ( ( 1024 * 1024 * g ) ) status = none ; done && \
ls - la rnd && du - sk rnd `
2020-09-09 10:50:59 -04:00
}
2018-09-05 22:11:04 -04:00
buf := bytes . NewBuffer ( nil )
w := tar . NewWriter ( buf )
writeTarRecord ( t , w , "Dockerfile" , dockerfile )
err := w . Close ( )
assert . NilError ( t , err )
apiclient := testEnv . APIClient ( )
resp , err := apiclient . ImageBuild ( ctx ,
buf ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
} )
out := bytes . NewBuffer ( nil )
assert . NilError ( t , err )
_ , err = io . Copy ( out , resp . Body )
resp . Body . Close ( )
assert . NilError ( t , err )
assert . Check ( t , is . Contains ( out . String ( ) , "Successfully built" ) )
}
2020-11-05 04:39:40 -05:00
func TestBuildWCOWSandboxSize ( t * testing . T ) {
2021-11-10 09:38:22 -05:00
t . Skip ( "FLAKY_TEST that needs to be fixed; see https://github.com/moby/moby/issues/42743" )
2020-11-05 04:39:40 -05:00
skip . If ( t , testEnv . DaemonInfo . OSType != "windows" , "only Windows has sandbox size control" )
ctx := context . TODO ( )
defer setupTest ( t ) ( )
dockerfile := ` FROM busybox AS intermediate
WORKDIR C : \ \ stuff
# Create and delete a 21 GB file
RUN fsutil file createnew C : \ \ stuff \ \ bigfile_0 . txt 22548578304 && del bigfile_0 . txt
# Create three 7 GB files
RUN fsutil file createnew C : \ \ stuff \ \ bigfile_1 . txt 7516192768
RUN fsutil file createnew C : \ \ stuff \ \ bigfile_2 . txt 7516192768
RUN fsutil file createnew C : \ \ stuff \ \ bigfile_3 . txt 7516192768
# Copy that 21 GB of data out into a new target
FROM busybox
COPY -- from = intermediate C : \ \ stuff C : \ \ stuff
`
buf := bytes . NewBuffer ( nil )
w := tar . NewWriter ( buf )
writeTarRecord ( t , w , "Dockerfile" , dockerfile )
err := w . Close ( )
assert . NilError ( t , err )
apiclient := testEnv . APIClient ( )
resp , err := apiclient . ImageBuild ( ctx ,
buf ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
} )
out := bytes . NewBuffer ( nil )
assert . NilError ( t , err )
_ , err = io . Copy ( out , resp . Body )
resp . Body . Close ( )
assert . NilError ( t , err )
2020-11-08 10:48:04 -05:00
// The test passes if either:
// - the image build succeeded; or
// - The "COPY --from=intermediate" step ran out of space during re-exec'd writing of the transport layer information to hcsshim's temp directory
// The latter case means we finished the COPY operation, so the sandbox must have been larger than 20GB, which was the test,
// and _then_ ran out of space on the host during `importLayer` in the WindowsFilter graph driver, while committing the layer.
// See https://github.com/moby/moby/pull/41636#issuecomment-723038517 for more details on the operations being done here.
// Specifically, this happens on the Docker Jenkins CI Windows-RS5 build nodes.
// The two parts of the acceptable-failure case are on different lines, so we need two regexp checks.
assert . Check ( t , is . Regexp ( "Successfully built|COPY --from=intermediate" , out . String ( ) ) )
assert . Check ( t , is . Regexp ( "Successfully built|re-exec error: exit status 1: output: write.*daemon\\\\\\\\tmp\\\\\\\\hcs.*bigfile_[1-3].txt: There is not enough space on the disk." , out . String ( ) ) )
2020-11-05 04:39:40 -05:00
}
2019-01-03 16:50:20 -05:00
func TestBuildWithEmptyDockerfile ( t * testing . T ) {
2019-04-19 13:12:52 -04:00
skip . If ( t , versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.40" ) , "broken in earlier versions" )
2019-01-03 16:50:20 -05:00
ctx := context . TODO ( )
defer setupTest ( t ) ( )
tests := [ ] struct {
name string
dockerfile string
expectedErr string
} {
{
name : "empty-dockerfile" ,
dockerfile : "" ,
expectedErr : "cannot be empty" ,
} ,
{
name : "empty-lines-dockerfile" ,
dockerfile : `
2021-08-24 06:10:50 -04:00
2019-01-03 16:50:20 -05:00
` ,
expectedErr : "file with no instructions" ,
} ,
{
name : "comment-only-dockerfile" ,
dockerfile : ` # this is a comment ` ,
expectedErr : "file with no instructions" ,
} ,
}
apiclient := testEnv . APIClient ( )
for _ , tc := range tests {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
buf := bytes . NewBuffer ( nil )
w := tar . NewWriter ( buf )
writeTarRecord ( t , w , "Dockerfile" , tc . dockerfile )
err := w . Close ( )
assert . NilError ( t , err )
_ , err = apiclient . ImageBuild ( ctx ,
buf ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
} )
assert . Check ( t , is . Contains ( err . Error ( ) , tc . expectedErr ) )
} )
}
}
2018-12-18 16:30:08 -05:00
func TestBuildPreserveOwnership ( t * testing . T ) {
skip . If ( t , testEnv . DaemonInfo . OSType == "windows" , "FIXME" )
skip . If ( t , versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.40" ) , "broken in earlier versions" )
ctx := context . Background ( )
2021-08-24 06:10:50 -04:00
dockerfile , err := os . ReadFile ( "testdata/Dockerfile." + t . Name ( ) )
2018-12-18 16:30:08 -05:00
assert . NilError ( t , err )
source := fakecontext . New ( t , "" , fakecontext . WithDockerfile ( string ( dockerfile ) ) )
defer source . Close ( )
apiclient := testEnv . APIClient ( )
for _ , target := range [ ] string { "copy_from" , "copy_from_chowned" } {
t . Run ( target , func ( t * testing . T ) {
resp , err := apiclient . ImageBuild (
ctx ,
source . AsTarReader ( t ) ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
Target : target ,
} ,
)
assert . NilError ( t , err )
out := bytes . NewBuffer ( nil )
_ , err = io . Copy ( out , resp . Body )
_ = resp . Body . Close ( )
if err != nil {
t . Log ( out )
}
assert . NilError ( t , err )
} )
}
}
2019-07-15 12:19:31 -04:00
func TestBuildPlatformInvalid ( t * testing . T ) {
skip . If ( t , versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.40" ) , "experimental in older versions" )
ctx := context . Background ( )
defer setupTest ( t ) ( )
dockerfile := ` FROM busybox
`
buf := bytes . NewBuffer ( nil )
w := tar . NewWriter ( buf )
writeTarRecord ( t , w , "Dockerfile" , dockerfile )
err := w . Close ( )
assert . NilError ( t , err )
apiclient := testEnv . APIClient ( )
_ , err = apiclient . ImageBuild ( ctx ,
buf ,
types . ImageBuildOptions {
Remove : true ,
ForceRemove : true ,
Platform : "foobar" ,
} )
assert . Assert ( t , err != nil )
assert . ErrorContains ( t , err , "unknown operating system or architecture" )
assert . Assert ( t , errdefs . IsInvalidParameter ( err ) )
}
2018-02-15 21:00:39 -05:00
func writeTarRecord ( t * testing . T , w * tar . Writer , fn , contents string ) {
err := w . WriteHeader ( & tar . Header {
Name : fn ,
Mode : 0600 ,
Size : int64 ( len ( contents ) ) ,
Typeflag : '0' ,
} )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2018-02-15 21:00:39 -05:00
_ , err = w . Write ( [ ] byte ( contents ) )
2018-03-13 15:28:34 -04:00
assert . NilError ( t , err )
2018-02-15 21:00:39 -05:00
}
2017-12-01 13:31:03 -05:00
type buildLine struct {
Stream string
Aux struct {
ID string
}
}
func getImageIDsFromBuild ( output [ ] byte ) ( [ ] string , error ) {
2018-05-19 07:38:54 -04:00
var ids [ ] string
2017-12-01 13:31:03 -05:00
for _ , line := range bytes . Split ( output , [ ] byte ( "\n" ) ) {
if len ( line ) == 0 {
continue
}
entry := buildLine { }
if err := json . Unmarshal ( line , & entry ) ; err != nil {
return nil , err
}
if entry . Aux . ID != "" {
ids = append ( ids , entry . Aux . ID )
}
}
return ids , nil
}