|
@@ -0,0 +1,130 @@
|
|
|
+package build
|
|
|
+
|
|
|
+import (
|
|
|
+ "archive/tar"
|
|
|
+ "bytes"
|
|
|
+ "context"
|
|
|
+ "encoding/json"
|
|
|
+ "io"
|
|
|
+ "strings"
|
|
|
+ "testing"
|
|
|
+
|
|
|
+ "github.com/docker/docker/api/types"
|
|
|
+ "github.com/docker/docker/api/types/filters"
|
|
|
+ "github.com/docker/docker/integration/util/request"
|
|
|
+ "github.com/docker/docker/pkg/jsonmessage"
|
|
|
+ "github.com/stretchr/testify/require"
|
|
|
+)
|
|
|
+
|
|
|
+func TestBuildWithRemoveAndForceRemove(t *testing.T) {
|
|
|
+ t.Parallel()
|
|
|
+ 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,
|
|
|
+ rm: false,
|
|
|
+ forceRm: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "successful build with remove",
|
|
|
+ dockerfile: `FROM busybox
|
|
|
+ RUN exit 0
|
|
|
+ RUN exit 0`,
|
|
|
+ numberOfIntermediateContainers: 0,
|
|
|
+ rm: true,
|
|
|
+ forceRm: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "successful build with remove and force remove",
|
|
|
+ dockerfile: `FROM busybox
|
|
|
+ RUN exit 0
|
|
|
+ RUN exit 0`,
|
|
|
+ numberOfIntermediateContainers: 0,
|
|
|
+ rm: true,
|
|
|
+ forceRm: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "failed build with no removal",
|
|
|
+ dockerfile: `FROM busybox
|
|
|
+ RUN exit 0
|
|
|
+ RUN exit 1`,
|
|
|
+ numberOfIntermediateContainers: 2,
|
|
|
+ rm: false,
|
|
|
+ forceRm: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "failed build with remove",
|
|
|
+ dockerfile: `FROM busybox
|
|
|
+ RUN exit 0
|
|
|
+ RUN exit 1`,
|
|
|
+ numberOfIntermediateContainers: 1,
|
|
|
+ rm: true,
|
|
|
+ forceRm: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "failed build with remove and force remove",
|
|
|
+ dockerfile: `FROM busybox
|
|
|
+ RUN exit 0
|
|
|
+ RUN exit 1`,
|
|
|
+ numberOfIntermediateContainers: 0,
|
|
|
+ rm: true,
|
|
|
+ forceRm: true,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ client := request.NewAPIClient(t)
|
|
|
+ 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)
|
|
|
+ require.NoError(t, tw.WriteHeader(&tar.Header{
|
|
|
+ Name: "Dockerfile",
|
|
|
+ Size: int64(len(dockerfile)),
|
|
|
+ }))
|
|
|
+ _, err := tw.Write(dockerfile)
|
|
|
+ require.NoError(t, err)
|
|
|
+ require.NoError(t, tw.Close())
|
|
|
+ resp, err := client.ImageBuild(ctx, buff, types.ImageBuildOptions{Remove: c.rm, ForceRemove: c.forceRm, NoCache: true})
|
|
|
+ require.NoError(t, err)
|
|
|
+ defer resp.Body.Close()
|
|
|
+ filter, err := buildContainerIdsFilter(resp.Body)
|
|
|
+ require.NoError(t, err)
|
|
|
+ remainingContainers, err := client.ContainerList(ctx, types.ContainerListOptions{Filters: filter, All: true})
|
|
|
+ require.NoError(t, err)
|
|
|
+ require.Equal(t, c.numberOfIntermediateContainers, len(remainingContainers), "Expected %v remaining intermediate containers, got %v", c.numberOfIntermediateContainers, len(remainingContainers))
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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):]))
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|