github.com/wozhu6104/docker@v20.10.10+incompatible/integration-cli/docker_cli_build_unix_test.go (about)

     1  // +build !windows
     2  
     3  package main
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"encoding/json"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"regexp"
    14  	"strings"
    15  	"syscall"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/docker/docker/integration-cli/cli"
    20  	"github.com/docker/docker/integration-cli/cli/build"
    21  	"github.com/docker/docker/testutil/fakecontext"
    22  	units "github.com/docker/go-units"
    23  	"gotest.tools/v3/assert"
    24  	"gotest.tools/v3/icmd"
    25  )
    26  
    27  func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *testing.T) {
    28  	testRequires(c, cpuCfsQuota)
    29  	name := "testbuildresourceconstraints"
    30  	buildLabel := "DockerSuite.TestBuildResourceConstraintsAreUsed"
    31  
    32  	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`
    33  	FROM hello-world:frozen
    34  	RUN ["/hello"]
    35  	`))
    36  	cli.Docker(
    37  		cli.Args("build", "--no-cache", "--rm=false", "--memory=64m", "--memory-swap=-1", "--cpuset-cpus=0", "--cpuset-mems=0", "--cpu-shares=100", "--cpu-quota=8000", "--ulimit", "nofile=42", "--label="+buildLabel, "-t", name, "."),
    38  		cli.InDir(ctx.Dir),
    39  	).Assert(c, icmd.Success)
    40  
    41  	out := cli.DockerCmd(c, "ps", "-lq", "--filter", "label="+buildLabel).Combined()
    42  	cID := strings.TrimSpace(out)
    43  
    44  	type hostConfig struct {
    45  		Memory     int64
    46  		MemorySwap int64
    47  		CpusetCpus string
    48  		CpusetMems string
    49  		CPUShares  int64
    50  		CPUQuota   int64
    51  		Ulimits    []*units.Ulimit
    52  	}
    53  
    54  	cfg := inspectFieldJSON(c, cID, "HostConfig")
    55  
    56  	var c1 hostConfig
    57  	err := json.Unmarshal([]byte(cfg), &c1)
    58  	assert.Assert(c, err == nil, cfg)
    59  
    60  	assert.Equal(c, c1.Memory, int64(64*1024*1024), "resource constraints not set properly for Memory")
    61  	assert.Equal(c, c1.MemorySwap, int64(-1), "resource constraints not set properly for MemorySwap")
    62  	assert.Equal(c, c1.CpusetCpus, "0", "resource constraints not set properly for CpusetCpus")
    63  	assert.Equal(c, c1.CpusetMems, "0", "resource constraints not set properly for CpusetMems")
    64  	assert.Equal(c, c1.CPUShares, int64(100), "resource constraints not set properly for CPUShares")
    65  	assert.Equal(c, c1.CPUQuota, int64(8000), "resource constraints not set properly for CPUQuota")
    66  	assert.Equal(c, c1.Ulimits[0].Name, "nofile", "resource constraints not set properly for Ulimits")
    67  	assert.Equal(c, c1.Ulimits[0].Hard, int64(42), "resource constraints not set properly for Ulimits")
    68  
    69  	// Make sure constraints aren't saved to image
    70  	cli.DockerCmd(c, "run", "--name=test", name)
    71  
    72  	cfg = inspectFieldJSON(c, "test", "HostConfig")
    73  
    74  	var c2 hostConfig
    75  	err = json.Unmarshal([]byte(cfg), &c2)
    76  	assert.Assert(c, err == nil, cfg)
    77  
    78  	assert.Assert(c, c2.Memory != int64(64*1024*1024), "resource leaked from build for Memory")
    79  	assert.Assert(c, c2.MemorySwap != int64(-1), "resource leaked from build for MemorySwap")
    80  	assert.Assert(c, c2.CpusetCpus != "0", "resource leaked from build for CpusetCpus")
    81  	assert.Assert(c, c2.CpusetMems != "0", "resource leaked from build for CpusetMems")
    82  	assert.Assert(c, c2.CPUShares != int64(100), "resource leaked from build for CPUShares")
    83  	assert.Assert(c, c2.CPUQuota != int64(8000), "resource leaked from build for CPUQuota")
    84  	assert.Assert(c, c2.Ulimits == nil, "resource leaked from build for Ulimits")
    85  }
    86  
    87  func (s *DockerSuite) TestBuildAddChangeOwnership(c *testing.T) {
    88  	testRequires(c, DaemonIsLinux)
    89  	name := "testbuildaddown"
    90  
    91  	ctx := func() *fakecontext.Fake {
    92  		dockerfile := `
    93  			FROM busybox
    94  			ADD foo /bar/
    95  			RUN [ $(stat -c %U:%G "/bar") = 'root:root' ]
    96  			RUN [ $(stat -c %U:%G "/bar/foo") = 'root:root' ]
    97  			`
    98  		tmpDir, err := ioutil.TempDir("", "fake-context")
    99  		assert.NilError(c, err)
   100  		testFile, err := os.Create(filepath.Join(tmpDir, "foo"))
   101  		if err != nil {
   102  			c.Fatalf("failed to create foo file: %v", err)
   103  		}
   104  		defer testFile.Close()
   105  
   106  		icmd.RunCmd(icmd.Cmd{
   107  			Command: []string{"chown", "daemon:daemon", "foo"},
   108  			Dir:     tmpDir,
   109  		}).Assert(c, icmd.Success)
   110  
   111  		if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
   112  			c.Fatalf("failed to open destination dockerfile: %v", err)
   113  		}
   114  		return fakecontext.New(c, tmpDir)
   115  	}()
   116  
   117  	defer ctx.Close()
   118  
   119  	buildImageSuccessfully(c, name, build.WithExternalBuildContext(ctx))
   120  }
   121  
   122  // Test that an infinite sleep during a build is killed if the client disconnects.
   123  // This test is fairly hairy because there are lots of ways to race.
   124  // Strategy:
   125  // * Monitor the output of docker events starting from before
   126  // * Run a 1-year-long sleep from a docker build.
   127  // * When docker events sees container start, close the "docker build" command
   128  // * Wait for docker events to emit a dying event.
   129  //
   130  // TODO(buildkit): this test needs to be rewritten for buildkit.
   131  // It has been manually tested positive. Confirmed issue: docker build output parsing.
   132  // Potential issue: newEventObserver uses docker events, which is not hooked up to buildkit.
   133  func (s *DockerSuite) TestBuildCancellationKillsSleep(c *testing.T) {
   134  	testRequires(c, DaemonIsLinux, TODOBuildkit)
   135  	name := "testbuildcancellation"
   136  
   137  	observer, err := newEventObserver(c)
   138  	assert.NilError(c, err)
   139  	err = observer.Start()
   140  	assert.NilError(c, err)
   141  	defer observer.Stop()
   142  
   143  	// (Note: one year, will never finish)
   144  	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile("FROM busybox\nRUN sleep 31536000"))
   145  	defer ctx.Close()
   146  
   147  	buildCmd := exec.Command(dockerBinary, "build", "-t", name, ".")
   148  	buildCmd.Dir = ctx.Dir
   149  
   150  	stdoutBuild, err := buildCmd.StdoutPipe()
   151  	assert.NilError(c, err)
   152  
   153  	if err := buildCmd.Start(); err != nil {
   154  		c.Fatalf("failed to run build: %s", err)
   155  	}
   156  	// always clean up
   157  	defer func() {
   158  		buildCmd.Process.Kill()
   159  		buildCmd.Wait()
   160  	}()
   161  
   162  	matchCID := regexp.MustCompile("Running in (.+)")
   163  	scanner := bufio.NewScanner(stdoutBuild)
   164  
   165  	outputBuffer := new(bytes.Buffer)
   166  	var buildID string
   167  	for scanner.Scan() {
   168  		line := scanner.Text()
   169  		outputBuffer.WriteString(line)
   170  		outputBuffer.WriteString("\n")
   171  		if matches := matchCID.FindStringSubmatch(line); len(matches) > 0 {
   172  			buildID = matches[1]
   173  			break
   174  		}
   175  	}
   176  
   177  	if buildID == "" {
   178  		c.Fatalf("Unable to find build container id in build output:\n%s", outputBuffer.String())
   179  	}
   180  
   181  	testActions := map[string]chan bool{
   182  		"start": make(chan bool, 1),
   183  		"die":   make(chan bool, 1),
   184  	}
   185  
   186  	matcher := matchEventLine(buildID, "container", testActions)
   187  	processor := processEventMatch(testActions)
   188  	go observer.Match(matcher, processor)
   189  
   190  	select {
   191  	case <-time.After(10 * time.Second):
   192  		observer.CheckEventError(c, buildID, "start", matcher)
   193  	case <-testActions["start"]:
   194  		// ignore, done
   195  	}
   196  
   197  	// Send a kill to the `docker build` command.
   198  	// Causes the underlying build to be cancelled due to socket close.
   199  	if err := buildCmd.Process.Kill(); err != nil {
   200  		c.Fatalf("error killing build command: %s", err)
   201  	}
   202  
   203  	// Get the exit status of `docker build`, check it exited because killed.
   204  	if err := buildCmd.Wait(); err != nil && !isKilled(err) {
   205  		c.Fatalf("wait failed during build run: %T %s", err, err)
   206  	}
   207  
   208  	select {
   209  	case <-time.After(10 * time.Second):
   210  		observer.CheckEventError(c, buildID, "die", matcher)
   211  	case <-testActions["die"]:
   212  		// ignore, done
   213  	}
   214  }
   215  
   216  func isKilled(err error) bool {
   217  	if exitErr, ok := err.(*exec.ExitError); ok {
   218  		status, ok := exitErr.Sys().(syscall.WaitStatus)
   219  		if !ok {
   220  			return false
   221  		}
   222  		// status.ExitStatus() is required on Windows because it does not
   223  		// implement Signal() nor Signaled(). Just check it had a bad exit
   224  		// status could mean it was killed (and in tests we do kill)
   225  		return (status.Signaled() && status.Signal() == os.Kill) || status.ExitStatus() != 0
   226  	}
   227  	return false
   228  }