Docker-DCO-1.1-Signed-off-by: Dan Walsh <dwalsh@redhat.com> (github: rhatdan)
| ... | ... |
@@ -244,8 +244,6 @@ func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *htt |
| 244 | 244 |
} |
| 245 | 245 |
|
| 246 | 246 |
func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 247 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 248 |
- |
|
| 249 | 247 |
v := &types.Version{
|
| 250 | 248 |
Version: dockerversion.VERSION, |
| 251 | 249 |
ApiVersion: api.APIVERSION, |
| ... | ... |
@@ -359,8 +357,6 @@ func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r |
| 359 | 359 |
} |
| 360 | 360 |
|
| 361 | 361 |
func (s *Server) getInfo(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 362 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 363 |
- |
|
| 364 | 362 |
info, err := s.daemon.SystemInfo() |
| 365 | 363 |
if err != nil {
|
| 366 | 364 |
return err |
| ... | ... |
@@ -863,7 +859,7 @@ func (s *Server) postImagesLoad(version version.Version, w http.ResponseWriter, |
| 863 | 863 |
|
| 864 | 864 |
func (s *Server) postContainersCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 865 | 865 |
if err := parseForm(r); err != nil {
|
| 866 |
- return nil |
|
| 866 |
+ return err |
|
| 867 | 867 |
} |
| 868 | 868 |
if err := checkForJson(r); err != nil {
|
| 869 | 869 |
return err |
| ... | ... |
@@ -1288,7 +1284,7 @@ func (s *Server) postContainersCopy(version version.Version, w http.ResponseWrit |
| 1288 | 1288 |
|
| 1289 | 1289 |
func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 1290 | 1290 |
if err := parseForm(r); err != nil {
|
| 1291 |
- return nil |
|
| 1291 |
+ return err |
|
| 1292 | 1292 |
} |
| 1293 | 1293 |
name := vars["name"] |
| 1294 | 1294 |
|
| ... | ... |
@@ -1317,7 +1313,7 @@ func (s *Server) postContainerExecCreate(version version.Version, w http.Respons |
| 1317 | 1317 |
// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. |
| 1318 | 1318 |
func (s *Server) postContainerExecStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 1319 | 1319 |
if err := parseForm(r); err != nil {
|
| 1320 |
- return nil |
|
| 1320 |
+ return err |
|
| 1321 | 1321 |
} |
| 1322 | 1322 |
var ( |
| 1323 | 1323 |
execName = vars["name"] |
| ... | ... |
@@ -437,7 +437,23 @@ func (container *Container) Kill() error {
|
| 437 | 437 |
|
| 438 | 438 |
// 1. Send SIGKILL |
| 439 | 439 |
if err := container.killPossiblyDeadProcess(9); err != nil {
|
| 440 |
- return err |
|
| 440 |
+ // While normally we might "return err" here we're not going to |
|
| 441 |
+ // because if we can't stop the container by this point then |
|
| 442 |
+ // its probably because its already stopped. Meaning, between |
|
| 443 |
+ // the time of the IsRunning() call above and now it stopped. |
|
| 444 |
+ // Also, since the err return will be exec driver specific we can't |
|
| 445 |
+ // look for any particular (common) error that would indicate |
|
| 446 |
+ // that the process is already dead vs something else going wrong. |
|
| 447 |
+ // So, instead we'll give it up to 2 more seconds to complete and if |
|
| 448 |
+ // by that time the container is still running, then the error |
|
| 449 |
+ // we got is probably valid and so we return it to the caller. |
|
| 450 |
+ |
|
| 451 |
+ if container.IsRunning() {
|
|
| 452 |
+ container.WaitStop(2 * time.Second) |
|
| 453 |
+ if container.IsRunning() {
|
|
| 454 |
+ return err |
|
| 455 |
+ } |
|
| 456 |
+ } |
|
| 441 | 457 |
} |
| 442 | 458 |
|
| 443 | 459 |
// 2. Wait for the process to die, in last resort, try to kill the process directly |
| ... | ... |
@@ -959,21 +959,37 @@ func (container *Container) DisableLink(name string) {
|
| 959 | 959 |
} |
| 960 | 960 |
|
| 961 | 961 |
func (container *Container) UnmountVolumes(forceSyscall bool) error {
|
| 962 |
- for _, m := range container.MountPoints {
|
|
| 963 |
- dest, err := container.GetResourcePath(m.Destination) |
|
| 962 |
+ var volumeMounts []mountPoint |
|
| 963 |
+ |
|
| 964 |
+ for _, mntPoint := range container.MountPoints {
|
|
| 965 |
+ dest, err := container.GetResourcePath(mntPoint.Destination) |
|
| 966 |
+ if err != nil {
|
|
| 967 |
+ return err |
|
| 968 |
+ } |
|
| 969 |
+ |
|
| 970 |
+ volumeMounts = append(volumeMounts, mountPoint{Destination: dest, Volume: mntPoint.Volume})
|
|
| 971 |
+ } |
|
| 972 |
+ |
|
| 973 |
+ for _, mnt := range container.networkMounts() {
|
|
| 974 |
+ dest, err := container.GetResourcePath(mnt.Destination) |
|
| 964 | 975 |
if err != nil {
|
| 965 | 976 |
return err |
| 966 | 977 |
} |
| 967 | 978 |
|
| 979 |
+ volumeMounts = append(volumeMounts, mountPoint{Destination: dest})
|
|
| 980 |
+ } |
|
| 981 |
+ |
|
| 982 |
+ for _, volumeMount := range volumeMounts {
|
|
| 968 | 983 |
if forceSyscall {
|
| 969 |
- syscall.Unmount(dest, 0) |
|
| 984 |
+ syscall.Unmount(volumeMount.Destination, 0) |
|
| 970 | 985 |
} |
| 971 | 986 |
|
| 972 |
- if m.Volume != nil {
|
|
| 973 |
- if err := m.Volume.Unmount(); err != nil {
|
|
| 987 |
+ if volumeMount.Volume != nil {
|
|
| 988 |
+ if err := volumeMount.Volume.Unmount(); err != nil {
|
|
| 974 | 989 |
return err |
| 975 | 990 |
} |
| 976 | 991 |
} |
| 977 | 992 |
} |
| 993 |
+ |
|
| 978 | 994 |
return nil |
| 979 | 995 |
} |
| ... | ... |
@@ -58,10 +58,6 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error |
| 58 | 58 |
|
| 59 | 59 |
// Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem. |
| 60 | 60 |
func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) {
|
| 61 |
- // stop collection of stats for the container regardless |
|
| 62 |
- // if stats are currently getting collected. |
|
| 63 |
- daemon.statsCollector.stopCollection(container) |
|
| 64 |
- |
|
| 65 | 61 |
if container.IsRunning() {
|
| 66 | 62 |
if !forceRemove {
|
| 67 | 63 |
return fmt.Errorf("Conflict, You cannot remove a running container. Stop the container before attempting removal or use -f")
|
| ... | ... |
@@ -71,6 +67,10 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) {
|
| 71 | 71 |
} |
| 72 | 72 |
} |
| 73 | 73 |
|
| 74 |
+ // stop collection of stats for the container regardless |
|
| 75 |
+ // if stats are currently getting collected. |
|
| 76 |
+ daemon.statsCollector.stopCollection(container) |
|
| 77 |
+ |
|
| 74 | 78 |
element := daemon.containers.Get(container.ID) |
| 75 | 79 |
if element == nil {
|
| 76 | 80 |
return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
|
| ... | ... |
@@ -8,7 +8,6 @@ import ( |
| 8 | 8 |
"path/filepath" |
| 9 | 9 |
"strings" |
| 10 | 10 |
|
| 11 |
- "github.com/docker/docker/daemon/execdriver" |
|
| 12 | 11 |
"github.com/docker/docker/pkg/chrootarchive" |
| 13 | 12 |
"github.com/docker/docker/runconfig" |
| 14 | 13 |
"github.com/docker/docker/volume" |
| ... | ... |
@@ -115,20 +114,6 @@ func validMountMode(mode string) bool {
|
| 115 | 115 |
return validModes[mode] |
| 116 | 116 |
} |
| 117 | 117 |
|
| 118 |
-func (container *Container) specialMounts() []execdriver.Mount {
|
|
| 119 |
- var mounts []execdriver.Mount |
|
| 120 |
- if container.ResolvConfPath != "" {
|
|
| 121 |
- mounts = append(mounts, execdriver.Mount{Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: !container.hostConfig.ReadonlyRootfs, Private: true})
|
|
| 122 |
- } |
|
| 123 |
- if container.HostnamePath != "" {
|
|
| 124 |
- mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: !container.hostConfig.ReadonlyRootfs, Private: true})
|
|
| 125 |
- } |
|
| 126 |
- if container.HostsPath != "" {
|
|
| 127 |
- mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: !container.hostConfig.ReadonlyRootfs, Private: true})
|
|
| 128 |
- } |
|
| 129 |
- return mounts |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 | 118 |
func copyExistingContents(source, destination string) error {
|
| 133 | 119 |
volList, err := ioutil.ReadDir(source) |
| 134 | 120 |
if err != nil {
|
| ... | ... |
@@ -254,6 +254,42 @@ func (s *DockerSuite) TestGetContainerStats(c *check.C) {
|
| 254 | 254 |
} |
| 255 | 255 |
} |
| 256 | 256 |
|
| 257 |
+func (s *DockerSuite) TestContainerStatsRmRunning(c *check.C) {
|
|
| 258 |
+ out, _ := dockerCmd(c, "run", "-d", "busybox", "top") |
|
| 259 |
+ id := strings.TrimSpace(out) |
|
| 260 |
+ |
|
| 261 |
+ buf := &channelBuffer{make(chan []byte, 1)}
|
|
| 262 |
+ defer buf.Close() |
|
| 263 |
+ chErr := make(chan error) |
|
| 264 |
+ go func() {
|
|
| 265 |
+ _, body, err := sockRequestRaw("GET", "/containers/"+id+"/stats?stream=1", nil, "application/json")
|
|
| 266 |
+ if err != nil {
|
|
| 267 |
+ chErr <- err |
|
| 268 |
+ } |
|
| 269 |
+ defer body.Close() |
|
| 270 |
+ _, err = io.Copy(buf, body) |
|
| 271 |
+ chErr <- err |
|
| 272 |
+ }() |
|
| 273 |
+ defer func() {
|
|
| 274 |
+ c.Assert(<-chErr, check.IsNil) |
|
| 275 |
+ }() |
|
| 276 |
+ |
|
| 277 |
+ b := make([]byte, 32) |
|
| 278 |
+ // make sure we've got some stats |
|
| 279 |
+ _, err := buf.ReadTimeout(b, 2*time.Second) |
|
| 280 |
+ c.Assert(err, check.IsNil) |
|
| 281 |
+ |
|
| 282 |
+ // Now remove without `-f` and make sure we are still pulling stats |
|
| 283 |
+ _, err = runCommand(exec.Command(dockerBinary, "rm", id)) |
|
| 284 |
+ c.Assert(err, check.Not(check.IsNil), check.Commentf("rm should have failed but didn't"))
|
|
| 285 |
+ _, err = buf.ReadTimeout(b, 2*time.Second) |
|
| 286 |
+ c.Assert(err, check.IsNil) |
|
| 287 |
+ dockerCmd(c, "rm", "-f", id) |
|
| 288 |
+ |
|
| 289 |
+ _, err = buf.ReadTimeout(b, 2*time.Second) |
|
| 290 |
+ c.Assert(err, check.Not(check.IsNil)) |
|
| 291 |
+} |
|
| 292 |
+ |
|
| 257 | 293 |
func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) {
|
| 258 | 294 |
// TODO: this test does nothing because we are c.Assert'ing in goroutine |
| 259 | 295 |
var ( |
| ... | ... |
@@ -595,3 +595,40 @@ func (s *DockerSuite) TestCpNameHasColon(c *check.C) {
|
| 595 | 595 |
c.Fatalf("Wrong content in copied file %q, should be %q", content, "lololol\n")
|
| 596 | 596 |
} |
| 597 | 597 |
} |
| 598 |
+ |
|
| 599 |
+func (s *DockerSuite) TestCopyAndRestart(c *check.C) {
|
|
| 600 |
+ expectedMsg := "hello" |
|
| 601 |
+ out, err := exec.Command(dockerBinary, "run", "-d", "busybox", "echo", expectedMsg).CombinedOutput() |
|
| 602 |
+ if err != nil {
|
|
| 603 |
+ c.Fatal(string(out), err) |
|
| 604 |
+ } |
|
| 605 |
+ id := strings.TrimSpace(string(out)) |
|
| 606 |
+ |
|
| 607 |
+ if out, err = exec.Command(dockerBinary, "wait", id).CombinedOutput(); err != nil {
|
|
| 608 |
+ c.Fatalf("unable to wait for container: %s", err)
|
|
| 609 |
+ } |
|
| 610 |
+ |
|
| 611 |
+ status := strings.TrimSpace(string(out)) |
|
| 612 |
+ if status != "0" {
|
|
| 613 |
+ c.Fatalf("container exited with status %s", status)
|
|
| 614 |
+ } |
|
| 615 |
+ |
|
| 616 |
+ tmpDir, err := ioutil.TempDir("", "test-docker-restart-after-copy-")
|
|
| 617 |
+ if err != nil {
|
|
| 618 |
+ c.Fatalf("unable to make temporary directory: %s", err)
|
|
| 619 |
+ } |
|
| 620 |
+ defer os.RemoveAll(tmpDir) |
|
| 621 |
+ |
|
| 622 |
+ if _, err = exec.Command(dockerBinary, "cp", fmt.Sprintf("%s:/etc/issue", id), tmpDir).CombinedOutput(); err != nil {
|
|
| 623 |
+ c.Fatalf("unable to copy from busybox container: %s", err)
|
|
| 624 |
+ } |
|
| 625 |
+ |
|
| 626 |
+ if out, err = exec.Command(dockerBinary, "start", "-a", id).CombinedOutput(); err != nil {
|
|
| 627 |
+ c.Fatalf("unable to start busybox container after copy: %s - %s", err, out)
|
|
| 628 |
+ } |
|
| 629 |
+ |
|
| 630 |
+ msg := strings.TrimSpace(string(out)) |
|
| 631 |
+ if msg != expectedMsg {
|
|
| 632 |
+ c.Fatalf("expected %q but got %q", expectedMsg, msg)
|
|
| 633 |
+ } |
|
| 634 |
+} |
| ... | ... |
@@ -42,11 +42,11 @@ func (s *DockerSuite) TestKillofStoppedContainer(c *check.C) {
|
| 42 | 42 |
|
| 43 | 43 |
stopCmd := exec.Command(dockerBinary, "stop", cleanedContainerID) |
| 44 | 44 |
out, _, err = runCommandWithOutput(stopCmd) |
| 45 |
- c.Assert(err, check.IsNil, check.Commentf("failed to stop container: %s, %v", out, err))
|
|
| 45 |
+ c.Assert(err, check.IsNil) |
|
| 46 | 46 |
|
| 47 | 47 |
killCmd := exec.Command(dockerBinary, "kill", "-s", "30", cleanedContainerID) |
| 48 | 48 |
_, _, err = runCommandWithOutput(killCmd) |
| 49 |
- c.Assert(err, check.Not(check.IsNil), check.Commentf("Kill succeeded on a stopped container"))
|
|
| 49 |
+ c.Assert(err, check.Not(check.IsNil), check.Commentf("Container %s is not running", cleanedContainerID))
|
|
| 50 | 50 |
} |
| 51 | 51 |
|
| 52 | 52 |
func (s *DockerSuite) TestKillDifferentUserContainer(c *check.C) {
|
| ... | ... |
@@ -3172,7 +3172,7 @@ func (s *DockerSuite) TestTwoContainersInNetHost(c *check.C) {
|
| 3172 | 3172 |
} |
| 3173 | 3173 |
|
| 3174 | 3174 |
func (s *DockerSuite) TestRunUnshareProc(c *check.C) {
|
| 3175 |
- testRequires(c, Apparmor) |
|
| 3175 |
+ testRequires(c, Apparmor, NativeExecDriver) |
|
| 3176 | 3176 |
|
| 3177 | 3177 |
name := "acidburn" |
| 3178 | 3178 |
runCmd := exec.Command(dockerBinary, "run", "--name", name, "jess/unshare", "unshare", "-p", "-m", "-f", "-r", "--mount-proc=/proc", "mount") |
| ... | ... |
@@ -7,7 +7,6 @@ import ( |
| 7 | 7 |
"errors" |
| 8 | 8 |
"fmt" |
| 9 | 9 |
"io" |
| 10 |
- "net/http" |
|
| 11 | 10 |
"net/http/httptest" |
| 12 | 11 |
"os" |
| 13 | 12 |
"os/exec" |
| ... | ... |
@@ -275,26 +274,6 @@ type FileServer struct {
|
| 275 | 275 |
*httptest.Server |
| 276 | 276 |
} |
| 277 | 277 |
|
| 278 |
-func fileServer(files map[string]string) (*FileServer, error) {
|
|
| 279 |
- var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
|
| 280 |
- if filePath, found := files[r.URL.Path]; found {
|
|
| 281 |
- http.ServeFile(w, r, filePath) |
|
| 282 |
- } else {
|
|
| 283 |
- http.Error(w, http.StatusText(404), 404) |
|
| 284 |
- } |
|
| 285 |
- } |
|
| 286 |
- |
|
| 287 |
- for _, file := range files {
|
|
| 288 |
- if _, err := os.Stat(file); err != nil {
|
|
| 289 |
- return nil, err |
|
| 290 |
- } |
|
| 291 |
- } |
|
| 292 |
- server := httptest.NewServer(handler) |
|
| 293 |
- return &FileServer{
|
|
| 294 |
- Server: server, |
|
| 295 |
- }, nil |
|
| 296 |
-} |
|
| 297 |
- |
|
| 298 | 278 |
// randomUnixTmpDirPath provides a temporary unix path with rand string appended. |
| 299 | 279 |
// does not create or checks if it exists. |
| 300 | 280 |
func randomUnixTmpDirPath(s string) string {
|
| ... | ... |
@@ -337,3 +316,26 @@ func parseCgroupPaths(procCgroupData string) map[string]string {
|
| 337 | 337 |
} |
| 338 | 338 |
return cgroupPaths |
| 339 | 339 |
} |
| 340 |
+ |
|
| 341 |
+type channelBuffer struct {
|
|
| 342 |
+ c chan []byte |
|
| 343 |
+} |
|
| 344 |
+ |
|
| 345 |
+func (c *channelBuffer) Write(b []byte) (int, error) {
|
|
| 346 |
+ c.c <- b |
|
| 347 |
+ return len(b), nil |
|
| 348 |
+} |
|
| 349 |
+ |
|
| 350 |
+func (c *channelBuffer) Close() error {
|
|
| 351 |
+ close(c.c) |
|
| 352 |
+ return nil |
|
| 353 |
+} |
|
| 354 |
+ |
|
| 355 |
+func (c *channelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) {
|
|
| 356 |
+ select {
|
|
| 357 |
+ case b := <-c.c: |
|
| 358 |
+ return copy(p[0:], b), nil |
|
| 359 |
+ case <-time.After(n): |
|
| 360 |
+ return -1, fmt.Errorf("timeout reading from channel")
|
|
| 361 |
+ } |
|
| 362 |
+} |