github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/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 "time" 17 18 "github.com/docker/docker/integration-cli/checker" 19 "github.com/docker/docker/integration-cli/cli" 20 "github.com/docker/docker/integration-cli/cli/build" 21 "github.com/docker/docker/internal/test/fakecontext" 22 "github.com/docker/go-units" 23 "github.com/go-check/check" 24 "gotest.tools/icmd" 25 ) 26 27 func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { 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 c.Assert(err, checker.IsNil, check.Commentf(cfg)) 59 60 c.Assert(c1.Memory, checker.Equals, int64(64*1024*1024), check.Commentf("resource constraints not set properly for Memory")) 61 c.Assert(c1.MemorySwap, checker.Equals, int64(-1), check.Commentf("resource constraints not set properly for MemorySwap")) 62 c.Assert(c1.CpusetCpus, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetCpus")) 63 c.Assert(c1.CpusetMems, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetMems")) 64 c.Assert(c1.CPUShares, checker.Equals, int64(100), check.Commentf("resource constraints not set properly for CPUShares")) 65 c.Assert(c1.CPUQuota, checker.Equals, int64(8000), check.Commentf("resource constraints not set properly for CPUQuota")) 66 c.Assert(c1.Ulimits[0].Name, checker.Equals, "nofile", check.Commentf("resource constraints not set properly for Ulimits")) 67 c.Assert(c1.Ulimits[0].Hard, checker.Equals, int64(42), check.Commentf("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 c.Assert(err, checker.IsNil, check.Commentf(cfg)) 77 78 c.Assert(c2.Memory, check.Not(checker.Equals), int64(64*1024*1024), check.Commentf("resource leaked from build for Memory")) 79 c.Assert(c2.MemorySwap, check.Not(checker.Equals), int64(-1), check.Commentf("resource leaked from build for MemorySwap")) 80 c.Assert(c2.CpusetCpus, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetCpus")) 81 c.Assert(c2.CpusetMems, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetMems")) 82 c.Assert(c2.CPUShares, check.Not(checker.Equals), int64(100), check.Commentf("resource leaked from build for CPUShares")) 83 c.Assert(c2.CPUQuota, check.Not(checker.Equals), int64(8000), check.Commentf("resource leaked from build for CPUQuota")) 84 c.Assert(c2.Ulimits, checker.IsNil, check.Commentf("resource leaked from build for Ulimits")) 85 } 86 87 func (s *DockerSuite) TestBuildAddChangeOwnership(c *check.C) { 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 c.Assert(err, check.IsNil) 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 *check.C) { 134 testRequires(c, DaemonIsLinux, TODOBuildkit) 135 name := "testbuildcancellation" 136 137 observer, err := newEventObserver(c) 138 c.Assert(err, checker.IsNil) 139 err = observer.Start() 140 c.Assert(err, checker.IsNil) 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 c.Assert(err, checker.IsNil) 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 }