github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/integration/build/build_test.go (about) 1 package 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/integration-cli/cli/build/fakecontext" 16 "github.com/docker/docker/integration/util/request" 17 "github.com/docker/docker/pkg/jsonmessage" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 ) 21 22 func TestBuildWithRemoveAndForceRemove(t *testing.T) { 23 defer setupTest(t)() 24 t.Parallel() 25 cases := []struct { 26 name string 27 dockerfile string 28 numberOfIntermediateContainers int 29 rm bool 30 forceRm bool 31 }{ 32 { 33 name: "successful build with no removal", 34 dockerfile: `FROM busybox 35 RUN exit 0 36 RUN exit 0`, 37 numberOfIntermediateContainers: 2, 38 rm: false, 39 forceRm: false, 40 }, 41 { 42 name: "successful build with remove", 43 dockerfile: `FROM busybox 44 RUN exit 0 45 RUN exit 0`, 46 numberOfIntermediateContainers: 0, 47 rm: true, 48 forceRm: false, 49 }, 50 { 51 name: "successful build with remove and force remove", 52 dockerfile: `FROM busybox 53 RUN exit 0 54 RUN exit 0`, 55 numberOfIntermediateContainers: 0, 56 rm: true, 57 forceRm: true, 58 }, 59 { 60 name: "failed build with no removal", 61 dockerfile: `FROM busybox 62 RUN exit 0 63 RUN exit 1`, 64 numberOfIntermediateContainers: 2, 65 rm: false, 66 forceRm: false, 67 }, 68 { 69 name: "failed build with remove", 70 dockerfile: `FROM busybox 71 RUN exit 0 72 RUN exit 1`, 73 numberOfIntermediateContainers: 1, 74 rm: true, 75 forceRm: false, 76 }, 77 { 78 name: "failed build with remove and force remove", 79 dockerfile: `FROM busybox 80 RUN exit 0 81 RUN exit 1`, 82 numberOfIntermediateContainers: 0, 83 rm: true, 84 forceRm: true, 85 }, 86 } 87 88 client := request.NewAPIClient(t) 89 ctx := context.Background() 90 for _, c := range cases { 91 t.Run(c.name, func(t *testing.T) { 92 t.Parallel() 93 dockerfile := []byte(c.dockerfile) 94 95 buff := bytes.NewBuffer(nil) 96 tw := tar.NewWriter(buff) 97 require.NoError(t, tw.WriteHeader(&tar.Header{ 98 Name: "Dockerfile", 99 Size: int64(len(dockerfile)), 100 })) 101 _, err := tw.Write(dockerfile) 102 require.NoError(t, err) 103 require.NoError(t, tw.Close()) 104 resp, err := client.ImageBuild(ctx, buff, types.ImageBuildOptions{Remove: c.rm, ForceRemove: c.forceRm, NoCache: true}) 105 require.NoError(t, err) 106 defer resp.Body.Close() 107 filter, err := buildContainerIdsFilter(resp.Body) 108 require.NoError(t, err) 109 remainingContainers, err := client.ContainerList(ctx, types.ContainerListOptions{Filters: filter, All: true}) 110 require.NoError(t, err) 111 require.Equal(t, c.numberOfIntermediateContainers, len(remainingContainers), "Expected %v remaining intermediate containers, got %v", c.numberOfIntermediateContainers, len(remainingContainers)) 112 }) 113 } 114 } 115 116 func buildContainerIdsFilter(buildOutput io.Reader) (filters.Args, error) { 117 const intermediateContainerPrefix = " ---> Running in " 118 filter := filters.NewArgs() 119 120 dec := json.NewDecoder(buildOutput) 121 for { 122 m := jsonmessage.JSONMessage{} 123 err := dec.Decode(&m) 124 if err == io.EOF { 125 return filter, nil 126 } 127 if err != nil { 128 return filter, err 129 } 130 if ix := strings.Index(m.Stream, intermediateContainerPrefix); ix != -1 { 131 filter.Add("id", strings.TrimSpace(m.Stream[ix+len(intermediateContainerPrefix):])) 132 } 133 } 134 } 135 136 func TestBuildMultiStageParentConfig(t *testing.T) { 137 dockerfile := ` 138 FROM busybox AS stage0 139 ENV WHO=parent 140 WORKDIR /foo 141 142 FROM stage0 143 ENV WHO=sibling1 144 WORKDIR sub1 145 146 FROM stage0 147 WORKDIR sub2 148 ` 149 ctx := context.Background() 150 source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile)) 151 defer source.Close() 152 153 apiclient := testEnv.APIClient() 154 resp, err := apiclient.ImageBuild(ctx, 155 source.AsTarReader(t), 156 types.ImageBuildOptions{ 157 Remove: true, 158 ForceRemove: true, 159 Tags: []string{"build1"}, 160 }) 161 require.NoError(t, err) 162 _, err = io.Copy(ioutil.Discard, resp.Body) 163 resp.Body.Close() 164 require.NoError(t, err) 165 166 image, _, err := apiclient.ImageInspectWithRaw(ctx, "build1") 167 require.NoError(t, err) 168 169 assert.Equal(t, "/foo/sub2", image.Config.WorkingDir) 170 assert.Contains(t, image.Config.Env, "WHO=parent") 171 } 172 173 func TestBuildWithEmptyLayers(t *testing.T) { 174 dockerfile := ` 175 FROM busybox 176 COPY 1/ /target/ 177 COPY 2/ /target/ 178 COPY 3/ /target/ 179 ` 180 ctx := context.Background() 181 source := fakecontext.New(t, "", 182 fakecontext.WithDockerfile(dockerfile), 183 fakecontext.WithFile("1/a", "asdf"), 184 fakecontext.WithFile("2/a", "asdf"), 185 fakecontext.WithFile("3/a", "asdf")) 186 defer source.Close() 187 188 apiclient := testEnv.APIClient() 189 resp, err := apiclient.ImageBuild(ctx, 190 source.AsTarReader(t), 191 types.ImageBuildOptions{ 192 Remove: true, 193 ForceRemove: true, 194 }) 195 require.NoError(t, err) 196 _, err = io.Copy(ioutil.Discard, resp.Body) 197 resp.Body.Close() 198 require.NoError(t, err) 199 }