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  }