| // +build !windows |
| |
| package main |
| |
| import ( |
| "bufio" |
| "bytes" |
| "encoding/json" |
| "io/ioutil" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "regexp" |
| "strings" |
| "time" |
| |
| "github.com/docker/docker/pkg/integration" |
| "github.com/docker/docker/pkg/integration/checker" |
| "github.com/docker/go-units" |
| "github.com/go-check/check" |
| ) |
| |
| func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { |
| testRequires(c, cpuCfsQuota) |
| name := "testbuildresourceconstraints" |
| |
| ctx, err := fakeContext(` |
| FROM hello-world:frozen |
| RUN ["/hello"] |
| `, map[string]string{}) |
| c.Assert(err, checker.IsNil) |
| |
| _, _, err = dockerCmdInDir(c, ctx.Dir, "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", "-t", name, ".") |
| if err != nil { |
| c.Fatal(err) |
| } |
| |
| out, _ := dockerCmd(c, "ps", "-lq") |
| cID := strings.TrimSpace(out) |
| |
| type hostConfig struct { |
| Memory int64 |
| MemorySwap int64 |
| CpusetCpus string |
| CpusetMems string |
| CPUShares int64 |
| CPUQuota int64 |
| Ulimits []*units.Ulimit |
| } |
| |
| cfg := inspectFieldJSON(c, cID, "HostConfig") |
| |
| var c1 hostConfig |
| err = json.Unmarshal([]byte(cfg), &c1) |
| c.Assert(err, checker.IsNil, check.Commentf(cfg)) |
| |
| c.Assert(c1.Memory, checker.Equals, int64(64*1024*1024), check.Commentf("resource constraints not set properly for Memory")) |
| c.Assert(c1.MemorySwap, checker.Equals, int64(-1), check.Commentf("resource constraints not set properly for MemorySwap")) |
| c.Assert(c1.CpusetCpus, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetCpus")) |
| c.Assert(c1.CpusetMems, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetMems")) |
| c.Assert(c1.CPUShares, checker.Equals, int64(100), check.Commentf("resource constraints not set properly for CPUShares")) |
| c.Assert(c1.CPUQuota, checker.Equals, int64(8000), check.Commentf("resource constraints not set properly for CPUQuota")) |
| c.Assert(c1.Ulimits[0].Name, checker.Equals, "nofile", check.Commentf("resource constraints not set properly for Ulimits")) |
| c.Assert(c1.Ulimits[0].Hard, checker.Equals, int64(42), check.Commentf("resource constraints not set properly for Ulimits")) |
| |
| // Make sure constraints aren't saved to image |
| dockerCmd(c, "run", "--name=test", name) |
| |
| cfg = inspectFieldJSON(c, "test", "HostConfig") |
| |
| var c2 hostConfig |
| err = json.Unmarshal([]byte(cfg), &c2) |
| c.Assert(err, checker.IsNil, check.Commentf(cfg)) |
| |
| c.Assert(c2.Memory, check.Not(checker.Equals), int64(64*1024*1024), check.Commentf("resource leaked from build for Memory")) |
| c.Assert(c2.MemorySwap, check.Not(checker.Equals), int64(-1), check.Commentf("resource leaked from build for MemorySwap")) |
| c.Assert(c2.CpusetCpus, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetCpus")) |
| c.Assert(c2.CpusetMems, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetMems")) |
| c.Assert(c2.CPUShares, check.Not(checker.Equals), int64(100), check.Commentf("resource leaked from build for CPUShares")) |
| c.Assert(c2.CPUQuota, check.Not(checker.Equals), int64(8000), check.Commentf("resource leaked from build for CPUQuota")) |
| c.Assert(c2.Ulimits, checker.IsNil, check.Commentf("resource leaked from build for Ulimits")) |
| } |
| |
| func (s *DockerSuite) TestBuildAddChangeOwnership(c *check.C) { |
| testRequires(c, DaemonIsLinux) |
| name := "testbuildaddown" |
| |
| ctx := func() *FakeContext { |
| dockerfile := ` |
| FROM busybox |
| ADD foo /bar/ |
| RUN [ $(stat -c %U:%G "/bar") = 'root:root' ] |
| RUN [ $(stat -c %U:%G "/bar/foo") = 'root:root' ] |
| ` |
| tmpDir, err := ioutil.TempDir("", "fake-context") |
| c.Assert(err, check.IsNil) |
| testFile, err := os.Create(filepath.Join(tmpDir, "foo")) |
| if err != nil { |
| c.Fatalf("failed to create foo file: %v", err) |
| } |
| defer testFile.Close() |
| |
| chownCmd := exec.Command("chown", "daemon:daemon", "foo") |
| chownCmd.Dir = tmpDir |
| out, _, err := runCommandWithOutput(chownCmd) |
| if err != nil { |
| c.Fatal(err, out) |
| } |
| |
| if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil { |
| c.Fatalf("failed to open destination dockerfile: %v", err) |
| } |
| return fakeContextFromDir(tmpDir) |
| }() |
| |
| defer ctx.Close() |
| |
| if _, err := buildImageFromContext(name, ctx, true); err != nil { |
| c.Fatalf("build failed to complete for TestBuildAddChangeOwnership: %v", err) |
| } |
| } |
| |
| // Test that an infinite sleep during a build is killed if the client disconnects. |
| // This test is fairly hairy because there are lots of ways to race. |
| // Strategy: |
| // * Monitor the output of docker events starting from before |
| // * Run a 1-year-long sleep from a docker build. |
| // * When docker events sees container start, close the "docker build" command |
| // * Wait for docker events to emit a dying event. |
| func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) { |
| testRequires(c, DaemonIsLinux) |
| name := "testbuildcancellation" |
| |
| observer, err := newEventObserver(c) |
| c.Assert(err, checker.IsNil) |
| err = observer.Start() |
| c.Assert(err, checker.IsNil) |
| defer observer.Stop() |
| |
| // (Note: one year, will never finish) |
| ctx, err := fakeContext("FROM busybox\nRUN sleep 31536000", nil) |
| if err != nil { |
| c.Fatal(err) |
| } |
| defer ctx.Close() |
| |
| buildCmd := exec.Command(dockerBinary, "build", "-t", name, ".") |
| buildCmd.Dir = ctx.Dir |
| |
| stdoutBuild, err := buildCmd.StdoutPipe() |
| if err := buildCmd.Start(); err != nil { |
| c.Fatalf("failed to run build: %s", err) |
| } |
| |
| matchCID := regexp.MustCompile("Running in (.+)") |
| scanner := bufio.NewScanner(stdoutBuild) |
| |
| outputBuffer := new(bytes.Buffer) |
| var buildID string |
| for scanner.Scan() { |
| line := scanner.Text() |
| outputBuffer.WriteString(line) |
| outputBuffer.WriteString("\n") |
| if matches := matchCID.FindStringSubmatch(line); len(matches) > 0 { |
| buildID = matches[1] |
| break |
| } |
| } |
| |
| if buildID == "" { |
| c.Fatalf("Unable to find build container id in build output:\n%s", outputBuffer.String()) |
| } |
| |
| testActions := map[string]chan bool{ |
| "start": make(chan bool, 1), |
| "die": make(chan bool, 1), |
| } |
| |
| matcher := matchEventLine(buildID, "container", testActions) |
| processor := processEventMatch(testActions) |
| go observer.Match(matcher, processor) |
| |
| select { |
| case <-time.After(10 * time.Second): |
| observer.CheckEventError(c, buildID, "start", matcher) |
| case <-testActions["start"]: |
| // ignore, done |
| } |
| |
| // Send a kill to the `docker build` command. |
| // Causes the underlying build to be cancelled due to socket close. |
| if err := buildCmd.Process.Kill(); err != nil { |
| c.Fatalf("error killing build command: %s", err) |
| } |
| |
| // Get the exit status of `docker build`, check it exited because killed. |
| if err := buildCmd.Wait(); err != nil && !integration.IsKilled(err) { |
| c.Fatalf("wait failed during build run: %T %s", err, err) |
| } |
| |
| select { |
| case <-time.After(10 * time.Second): |
| observer.CheckEventError(c, buildID, "die", matcher) |
| case <-testActions["die"]: |
| // ignore, done |
| } |
| } |