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  }