Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't create source directory while the daemon is being shutdown, fix #30348 #33330

Merged
merged 1 commit into from Jun 7, 2017

Conversation

coolljt0725
Copy link
Contributor

If a container mount the socket the daemon is listening on into
container while the daemon is being shutdown, the socket will
not exist on the host, then daemon will assume it's a directory
and create it on the host, this will cause the daemon can't start
next time.

fix issue #30348

To reproduce this issue, you can add following code

--- a/daemon/oci_linux.go
+++ b/daemon/oci_linux.go
@@ -8,6 +8,7 @@ import (
        "sort"
        "strconv"
        "strings"
+       "time"

        "github.com/Sirupsen/logrus"
        "github.com/docker/docker/container"
@@ -666,7 +667,8 @@ func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, e
        if err := daemon.setupIpcDirs(c); err != nil {
                return nil, err
        }
-
+       fmt.Printf("===please stop the daemon===\n")
+       time.Sleep(time.Second * 2)
        ms, err := daemon.setupMounts(c)
        if err != nil {
                return nil, err

step1 run a container which has --restart always and -v /var/run/docker.sock:/sock

$ docker run -ti --restart always -v /var/run/docker.sock:/sock busybox
/ #

step2 exit the the container

/ # exit

and kill the daemon when you see

===please stop the daemon===

in the daemon log

The daemon can't restart again and fail with can't create unix socket /var/run/docker.sock: is a directory.

Signed-off-by: Lei Jitang leijitang@huawei.com

ping @cpuguy83 @thaJeztah

- What I did

- How I did it

- How to verify it

- Description for the changelog

- A picture of a cute animal (not mandatory but encouraged)

@thaJeztah
Copy link
Member

@coolljt0725 looks like build is failing;

11:26:03 # github.com/docker/docker/daemon
11:26:03 daemon/volumes_windows.go:27: not enough arguments in call to mount.Setup
11:26:03 	have (string, number, number)
11:26:03 	want (string, int, int, func() error)

@coolljt0725
Copy link
Contributor Author

@thaJeztah updated

@thaJeztah
Copy link
Member

ping @cpuguy83 @mlaventure PTAL

Copy link
Contributor

@mlaventure mlaventure left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits and a requested change to checkFun prototype.

Otherwise, LGTM. Thanks for finding this!

@@ -42,8 +43,21 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
if err := daemon.lazyInitializeVolume(c.ID, m); err != nil {
return nil, err
}
// if the daemon is being shutdown, we could not let the container
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

If the daemon is being shutdown, we should not let a container start if it is trying to mount the socket the daemon is listening on. During daemon shutdown, the socket (/var/run/docker.sock by default) doesn't exist anymore causing the call to m.Setup to create at directory instead. This in turn will prevent the daemon to restart.

// the socket (`/var/run/docker.sock` by default) does not exist and
// the m.Setup will assume it's a directory and create it, this will
// cause the daemon can't start next time.
checkfunc := func() error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer the callback to be of the form func(m *Mountpoint) error

volume/volume.go Outdated
@@ -146,7 +146,9 @@ func (m *MountPoint) Cleanup() error {

// Setup sets up a mount point by either mounting the volume if it is
// configured, or creating the source directory if supplied.
func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int) (path string, err error) {
// The checkFun is to do some checking before creating the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

The, optional, checkFun parameter allows doing additional checking before creating the source directory on the host.

volume/volume.go Outdated
@@ -181,6 +183,14 @@ func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int) (path string

// system.MkdirAll() produces an error if m.Source exists and is a file (not a directory),
if m.Type == mounttypes.TypeBind {
// Before creating source directory on the host do the checking if it's not nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Before creating the source directory on the host, invoke checkFun if it's not nil. One of the use case is to forbid creating the daemon socket as a directory if the daemon is in the process of shutting down.

@coolljt0725
Copy link
Contributor Author

@mlaventure update

…oby#30348

If a container mount the socket the daemon is listening on into
container while the daemon is being shutdown, the socket will
not exist on the host, then daemon will assume it's a directory
and create it on the host, this will cause the daemon can't start
next time.

fix issue moby#30348

To reproduce this issue, you can add following code

```
--- a/daemon/oci_linux.go
+++ b/daemon/oci_linux.go
@@ -8,6 +8,7 @@ import (
        "sort"
        "strconv"
        "strings"
+       "time"

        "github.com/Sirupsen/logrus"
        "github.com/docker/docker/container"
@@ -666,7 +667,8 @@ func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, e
        if err := daemon.setupIpcDirs(c); err != nil {
                return nil, err
        }
-
+       fmt.Printf("===please stop the daemon===\n")
+       time.Sleep(time.Second * 2)
        ms, err := daemon.setupMounts(c)
        if err != nil {
                return nil, err

```

step1 run a container which has `--restart always` and `-v /var/run/docker.sock:/sock`
```
$ docker run -ti --restart always -v /var/run/docker.sock:/sock busybox
/ #

```
step2 exit the the container
```
/ # exit
```
and kill the daemon when you see
```
===please stop the daemon===
```
in the daemon log

The daemon can't restart again and fail with `can't create unix socket /var/run/docker.sock: is a directory`.

Signed-off-by: Lei Jitang <leijitang@huawei.com>
Copy link
Contributor

@mlaventure mlaventure left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Thanks!

// StoreHosts stores the addresses the daemon is listening on
func (daemon *Daemon) StoreHosts(hosts []string) {
if daemon.hosts == nil {
daemon.hosts = make(map[string]bool)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we initialize daemon.hosts in NewDaemon()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have considered that, but it will have some duplicate code to dealing with the hosts config (https://github.com/moby/moby/blob/master/cmd/dockerd/daemon.go#L157).

Copy link
Member

@AkihiroSuda AkihiroSuda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants