Skip to content

Skip rootfs unmount when no mounts are provided#3148

Merged
crosbymichael merged 3 commits intocontainerd:masterfrom
masters-of-cats:wip-rootless-containerd
May 7, 2019
Merged

Skip rootfs unmount when no mounts are provided#3148
crosbymichael merged 3 commits intocontainerd:masterfrom
masters-of-cats:wip-rootless-containerd

Conversation

@georgethebeatle
Copy link
Copy Markdown
Contributor

We (Cloud Foundry Garden) are trying to run containerd as a non-root user for extra security. We have our own component for creating container images so we are not using a snapshotter. While this mostly works we hit the following problem:

  • Currently when you create a new task, the client will ask the snapshotter (if any) about any mount points that containerd should make and pass them on with the request so that they are performed by the shim over here. If there is no snapshotter the request will contain an empty list of mounts and the shim will not perform any mounts
  • When you destroy the task, however, the shim will try to unconditionally unmount those mountpoints which were persisted in a filed of the process struct.

As of today this works because containerd is usually run as uid 0 in its respective user namespace, but honestly it feels a bit asymmetric and is breaking our approach to rootless containers which is to run the containerd daemon as a non-root user.

We have no issues with task creation because we are not using a snapshotter, so as mentioned above no mount calls are performed by the shim. However, when we attempt a destroy we hit the unconditional unmount call and as you can imagine this results in an operation not permitted error.

This PR suggests a fix which makes sure that the shim will not do any unmount calls when there is nothing to unmount (no rootfs mounts were passed in to the request, because no snapshotter was used).

Any comments and suggestions are welcome.

Co-authored-by: Julia Nedialkova [email protected]
Signed-off-by: Julia Nedialkova [email protected]

@georgethebeatle georgethebeatle force-pushed the wip-rootless-containerd branch 2 times, most recently from 676e0f9 to ff7e0d4 Compare March 29, 2019 14:57
@codecov-io
Copy link
Copy Markdown

Codecov Report

Merging #3148 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #3148   +/-   ##
=======================================
  Coverage   45.16%   45.16%           
=======================================
  Files         111      111           
  Lines       11962    11962           
=======================================
  Hits         5403     5403           
  Misses       5727     5727           
  Partials      832      832
Flag Coverage Δ
#linux 49.25% <ø> (ø) ⬆️
#windows 40.49% <ø> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e7b6fea...ff7e0d4. Read the comment docs.

@codecov-io
Copy link
Copy Markdown

codecov-io commented Mar 29, 2019

Codecov Report

Merging #3148 into master will increase coverage by 0.08%.
The diff coverage is 0%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3148      +/-   ##
==========================================
+ Coverage   45.14%   45.22%   +0.08%     
==========================================
  Files         111      111              
  Lines       11971    11994      +23     
==========================================
+ Hits         5404     5424      +20     
- Misses       5733     5736       +3     
  Partials      834      834
Flag Coverage Δ
#linux 49.25% <0%> (+0.03%) ⬆️
#windows 40.62% <ø> (+0.15%) ⬆️
Impacted Files Coverage Δ
mount/mount_linux.go 30.24% <0%> (-0.77%) ⬇️
runtime/v2/shim/shim_unix.go 0% <0%> (ø) ⬆️
content/local/store.go 49.51% <0%> (+0.99%) ⬆️
remotes/docker/fetcher.go 64.58% <0%> (+10.23%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e16368d...ae5ca81. Read the comment docs.

@thaJeztah
Copy link
Copy Markdown
Member

/cc @AkihiroSuda

Comment thread runtime/v1/shim/service.go Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

rootfs should be initialized as an empty string and set to filepath.Join(r.Bundle, "rootfs") only when len(mounts) > 0?

@AkihiroSuda
Copy link
Copy Markdown
Member

LGTM except a nit

v2 runtime needs this fix as well? (Can be a separate PR)

We have no issues with task creation because we are not using a snapshotter, so as mentioned above no mount calls are performed by the shim.

We should have integration test suites for both "root in userns" and "non-root outside userns" mode so as to clarify what are expected to work and what are not.

@AkihiroSuda
Copy link
Copy Markdown
Member

nit: Co-authored-by doesn't seem to be needed if this commit was solely authored by Julia

@fuweid
Copy link
Copy Markdown
Member

fuweid commented Mar 30, 2019

hi @georgethebeatle ,

The current implementation ignore the not mount point when the rootfs is provided. So I think we can think it in this way, check rootfs is provided or not. WDYT?

and it seems that we cannot mount without sudo and maybe we can add some user in /etc/fstab. just random thoughts.

Co-authored-by: Julia Nedialkova <[email protected]>
Signed-off-by: Georgi Sabev <[email protected]>
@georgethebeatle georgethebeatle force-pushed the wip-rootless-containerd branch from ff7e0d4 to 13d9ce9 Compare April 4, 2019 15:21
@georgethebeatle
Copy link
Copy Markdown
Contributor Author

@fuweid If you pass rootfs mounts using the WithRootFS task option it would still be mounted by the shim. However this will not work rootlessly and this is an intentional limitation of this PR.

@georgethebeatle
Copy link
Copy Markdown
Contributor Author

@AkihiroSuda we have addressed your comments and v2 is now supported.

We should have integration test suites for both "root in userns" and "non-root outside userns" mode so as to clarify what are expected to work and what are not.

Do you think that adding those tests is in the scope of this PR or is this a suggestion for future work?

* Rootfs dir is created during container creation not during bundle
  creation
* Add support for v2
* UnmountAll is a no-op when the path to unmount (i.e. the rootfs dir)
  does not exist or is invalid

Co-authored-by: Danail Branekov <[email protected]>
Signed-off-by: Georgi Sabev <[email protected]>
@georgethebeatle georgethebeatle force-pushed the wip-rootless-containerd branch from 13d9ce9 to c0f0b21 Compare April 4, 2019 15:40
@fuweid
Copy link
Copy Markdown
Member

fuweid commented Apr 4, 2019

@georgethebeatle you can try oci.WithRootFSPath which shim will not mount it , because it is provided by client. But the shim still tries to unmount it after cleanup. That is the problem. If we can know that it is not mount point, we can skip it.

@AkihiroSuda
Copy link
Copy Markdown
Member

Test can be future work

@georgethebeatle
Copy link
Copy Markdown
Contributor Author

@fuweid If you provide any mounts via oci.WithRootFSPath the shim WILL try to mount them, because the client will add them to the request here and later on the shim will range over them here. The shim will always call mount.UnmountAll on cleanup but note that we have modified that function to not do the unmount if the mount argument is not a valid path. We have also made sure that in case no mounts were propagated to the shim by the client the rootfs will be set to "" and no rootfs directory will be created in the bundle dir. This way when UnmountAll is called it will return early and not attempt to do the unmount.

As far as I know we cannot check if the rootfs directory is a mount point as a non-root user, because it will involve making a mount syscall and checking if the error we got back was EINVAL, but as a non root user we are not allowed to call mount so we always get an EPERM. That's why we have taken the approach described above.

@georgethebeatle
Copy link
Copy Markdown
Contributor Author

@AkihiroSuda @fuweid Do you think the PR is mergeable as it is or do we need to do some more work?

Comment thread mount/mount_linux.go
// useful for undoing a stack of mounts on the same mount point.
func UnmountAll(mount string, flags int) error {
if _, err := os.Stat(mount); err != nil {
return nil
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

return err? or needs os.IsNotExist check?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We thought the same, but when you do os.Stat("") it returns an EINVAL error as you can see. We want to skip the unmount in 2 cases - if it is an invalid path ("") or if it does not exist on disk. That's why we do not do the os.IsNotExist check. Our thinking was that If you cannot stat the directory it is doubtful if you will be able to unmount it anyway.

If you prefer we can have 2 checks instead - a check for an empty string and an os.IsNotExist check. What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

+1 for having 2 checks.
The code should also have comment lines about this.

cc @dmcgowan @fuweid

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we can check the rootfs in the shim, not in mount package. mount package don't need to carry this logic.

Co-authored-by: Danail Branekov <[email protected]>
Signed-off-by: Georgi Sabev <[email protected]>
@fuweid
Copy link
Copy Markdown
Member

fuweid commented Apr 10, 2019

@georgethebeatle sorry for the late reply.

If you provide any mounts via oci.WithRootFSPath the shim WILL try to mount them, because the client will add them to the request here and later on the shim will range over them here.

If I understand correctly, the oci.WithRootFSPath is used to set the rootfs in oci runtime spec. shim will no mount it.

# run with root mode

# no mount point here
➜  go mount | grep sha256

# use provided rootfs - busybox layer
➜  go ctr run -d --fifo-dir /tmp/1 --rootfs /home/vagrant/blobs/sha256 demo top

# check task is running
➜  go ctr task ls
TASK    PID     STATUS
demo    5611    RUNNING

# no mount
➜  go mount | grep sha256

As far as I know we cannot check if the rootfs directory is a mount point as a non-root user, because it will involve making a mount syscall and checking if the error we got back was EINVAL, but as a non root user we are not allowed to call mount so we always get an EPERM.

The /proc/mounts can be read by non-root user. So I try to read the /proc/mounts info and check the path in there or not. I am not sure that it is good way. I make demo for this. Please check 9ed7065

I use the following command to run it and it will not unmount the provided rootfs.

# busybox image and ignore the rootless container error :P
$ ctr -a /tmp/containerd.sock run -t --fifo-dir /tmp/1 --rootfs /home/vagrant/blobs/sha256 demo1 ls

ctr: OCI runtime create failed: rootless container requires user namespaces: unknown

cc @AkihiroSuda

@danail-branekov
Copy link
Copy Markdown
Contributor

@fuweid Your suggestion to check whether a path is a mountpoint prior unmounting is racy in the rootful case. In a concurrent scenario where two deletes are running in parallel it may happen that deleteA has checked whether something is a mountpoint and is about to unmount it. In the meanwhile, deleteB might have already umounted the path thus making deleteA unmount to fail (as the path has been already unmounted). In order to avoid the race we unmount straight away and ignore the EINVAL (i.e. not a mountpoint error).
In the rootless scenario we would not want to unmount at all (as we are running as an unprivileged user). In order to do that we use the empty string as a special marker value to indicate that there is no rootfs mount. I think that this is the simpler solution compared to checking the mount table and taking the risk of being racy.
Agree?

@georgethebeatle
Copy link
Copy Markdown
Contributor Author

@fuweid We applied the change you suggested adding some debug logging:

diff --git a/mount/mount_linux.go b/mount/mount_linux.go
index b5a16148..86f4e87b 100644
--- a/mount/mount_linux.go
+++ b/mount/mount_linux.go
@@ -18,6 +18,7 @@ package mount

 import (
  "fmt"
+ "io/ioutil"
  "os"
  "path"
  "strings"
@@ -112,6 +113,16 @@ func unmount(target string, flags int) error {
 // are no mounts remaining (EINVAL is returned by mount), which is
 // useful for undoing a stack of mounts on the same mount point.
 func UnmountAll(mount string, flags int) error {
+ isMount, err := isMountpoint(mount)
+ if err != nil {
+   return err
+ }
+
+ if !isMount {
+   fmt.Printf("Skipping unmount of: %s\n", mount)
+   return nil
+ }
+
  for {
    if err := unmount(mount, flags); err != nil {
      // EINVAL is returned if the target is not a
@@ -127,6 +138,32 @@ func UnmountAll(mount string, flags int) error {
  }
 }

+func isMountpoint(path string) (bool, error) {
+ bs, err := ioutil.ReadFile("/proc/mounts")
+ if err != nil {
+   return false, err
+ }
+
+ lines := strings.Split(string(bs), "\n")
+ for _, line := range lines {
+   if line == "" {
+     continue
+   }
+
+   fields := strings.Fields(line)
+   if len(fields) != 6 {
+     continue
+   }
+
+   if fields[1] == path {
+     return true, nil
+   }
+ }
+
+ fmt.Printf("\n Mount table is \n%s\n\n", string(bs))
+ return false, nil
+}
+
 // parseMountOptions takes fstab style mount options and parses them for
 // use with a standard mount() syscall
 func parseMountOptions(options []string) (int, string) {

Then we ran containerd's integration tests for the v2 runtime with the following command:

for i in {0..100}; do printf "\n\n\n\n\n ################## ATTEMPT %d\n\n\n\n\n\n" $i; TEST_RUNTIME=io.containerd.runc.v2 make integration || break; done

It failed on the 80th run with the following error:

 Mount table is
sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
udev /dev devtmpfs rw,nosuid,relatime,size=1012144k,nr_inodes=253036,mode=755 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
tmpfs /run tmpfs rw,nosuid,noexec,relatime,size=204104k,mode=755 0 0
/dev/sda1 / ext4 rw,relatime,data=ordered 0 0
securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0
tmpfs /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0
tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0
cgroup /sys/fs/cgroup/unified cgroup2 rw,nosuid,nodev,noexec,relatime 0 0
cgroup /sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,xattr,name=systemd 0 0
pstore /sys/fs/pstore pstore rw,nosuid,nodev,noexec,relatime 0 0
cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0
cgroup /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0
cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0
cgroup /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0
cgroup /sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0
cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0
cgroup /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0
cgroup /sys/fs/cgroup/rdma cgroup rw,nosuid,nodev,noexec,relatime,rdma 0 0
cgroup /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0
cgroup /sys/fs/cgroup/hugetlb cgroup rw,nosuid,nodev,noexec,relatime,hugetlb 0 0
cgroup /sys/fs/cgroup/pids cgroup rw,nosuid,nodev,noexec,relatime,pids 0 0
systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime,fd=24,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=12599 0 0
hugetlbfs /dev/hugepages hugetlbfs rw,relatime,pagesize=2M 0 0
debugfs /sys/kernel/debug debugfs rw,relatime 0 0
mqueue /dev/mqueue mqueue rw,relatime 0 0
fusectl /sys/fs/fuse/connections fusectl rw,relatime 0 0
configfs /sys/kernel/config configfs rw,relatime 0 0
lxcfs /var/lib/lxcfs fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
vagrant /vagrant vboxsf rw,nodev,relatime 0 0
vagrant_data /vagrant_data vboxsf rw,nodev,relatime 0 0
tmpfs /run/user/1000 tmpfs rw,nosuid,nodev,relatime,size=204100k,mode=700,uid=1000,gid=1000 0 0
/dev/sda1 /tmp/snapshot-suite-SnapshotterClient-791538560/work/prepare091312845 ext4 rw,relatime,data=ordered 0 0
/dev/sda1 /tmp/snapshot-suite-SnapshotterClient-039602127/work/check973865297 ext4 ro,relatime,data=ordered 0 0
overlay /tmp/snapshot-suite-SnapshotterClient-511760647/work/nextnextlayer overlay ro,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/20/fs:/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/15/fs 0 0
/dev/sda1 /tmp/snapshot-suite-SnapshotterClient-991477075/work/preparing ext4 rw,relatime,data=ordered 0 0
overlay /tmp/snapshot-suite-SnapshotterClient-991477075/work/nextlayer overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/9/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/17/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/17/work 0 0
overlay /tmp/snapshot-suite-SnapshotterClient-991477075/work/nextnextlayer overlay ro,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/17/fs:/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/9/fs 0 0


Skipping unmount of: /tmp/snapshot-suite-SnapshotterClient-979746360/work/prepare954751792
=== CONT  TestSnapshotterClient/PreareViewFailingtest
=== CONT  TestSnapshotterClient/StatActive
=== CONT  TestSnapshotterClient/StatComitted
=== CONT  TestSnapshotterClient/TransitivityTest
=== CONT  TestSnapshotterClient/RemoveIntermediateSnapshot
=== CONT  TestSnapshotterClient/MoveFileFromLowerLayer
=== CONT  TestSnapshotterClient/128LayersMount
=== CONT  TestSnapshotterClient/StatInWalk
=== CONT  TestSnapshotterClient/ViewReadonly
=== CONT  TestSnapshotterClient/Rename
--- FAIL: TestSnapshotterClient (0.02s)
    --- PASS: TestSnapshotterClient/RootPermission (0.07s)
        helpers_unix.go:32: unmount /tmp/snapshot-suite-SnapshotterClient-334983933/work/preparing
    --- PASS: TestSnapshotterClient/CloseTwice (0.08s)
    --- FAIL: TestSnapshotterClient/Chown (0.36s)
        issues.go:111: Check snapshots failed: directory diff between /tmp/snapshot-suite-SnapshotterClient-979746360/work/flat255284778 and /tmp/snapshot-suite-SnapshotterClient-979746360/work/check202078681
                - /opt
                - /opt/a
                - /opt/a/b
                - /opt/a/b/file.txt

                github.com/containerd/containerd/vendor/github.com/containerd/continuity/fs/fstest.CheckDirectoryEqual
                        /vagrant_data/src/github.com/containerd/containerd/vendor/github.com/containerd/continuity/fs/fstest/compare.go:53
                github.com/containerd/containerd/snapshots/testsuite.checkSnapshot
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/helpers.go:104
                github.com/containerd/containerd/snapshots/testsuite.checkSnapshots
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/helpers.go:132
                github.com/containerd/containerd/snapshots/testsuite.checkChown
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/issues.go:110
                github.com/containerd/containerd/snapshots/testsuite.makeTest.func1
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/testsuite.go:111
                testing.tRunner
                        /usr/local/go/src/testing/testing.go:777
                runtime.goexit
                        /usr/local/go/src/runtime/asm_amd64.s:2361
                check directory failed
                github.com/containerd/containerd/snapshots/testsuite.checkSnapshot
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/helpers.go:105
                github.com/containerd/containerd/snapshots/testsuite.checkSnapshots
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/helpers.go:132
                github.com/containerd/containerd/snapshots/testsuite.checkChown
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/issues.go:110
                github.com/containerd/containerd/snapshots/testsuite.makeTest.func1
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/testsuite.go:111
                testing.tRunner
                        /usr/local/go/src/testing/testing.go:777
                runtime.goexit
                        /usr/local/go/src/runtime/asm_amd64.s:2361
                snapshot check failed on snapshot 2
                github.com/containerd/containerd/snapshots/testsuite.checkSnapshots
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/helpers.go:133
                github.com/containerd/containerd/snapshots/testsuite.checkChown
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/issues.go:110
                github.com/containerd/containerd/snapshots/testsuite.makeTest.func1
                        /vagrant_data/src/github.com/containerd/containerd/snapshots/testsuite/testsuite.go:111
                testing.tRunner
                        /usr/local/go/src/testing/testing.go:777
                runtime.goexit
                        /usr/local/go/src/runtime/asm_amd64.s:2361
        helpers.go:67: drwx------       4096 /tmp/snapshot-suite-SnapshotterClient-979746360
        helpers.go:67: drwxr-xr-x       4096 /tmp/snapshot-suite-SnapshotterClient-979746360/root
        helpers.go:67: drwxr-xr-x       4096 /tmp/snapshot-suite-SnapshotterClient-979746360/work
        helpers.go:67: drwxr-xr-x       4096 /tmp/snapshot-suite-SnapshotterClient-979746360/work/prepare954751792

Looking at our debug logs it seems like UnmountAll skipped the unmounting of /tmp/snapshot-suite-SnapshotterClient-979746360/work/prepare954751792, because it was not in the mount table at the time of the check. Later on the test is making a diff of some directories and unexpectedly finds /tmp/snapshot-suite-SnapshotterClient-979746360/work/prepare954751792 which should have been removed, but wasn't because it has not been unmounted. The fact that it was not unmounted is confirmed by Skipping unmount of debug line, which appears only in the failing test logs. This line is missing in all the cases when the very same test passes.

We did the same experiment using the docker mount.GetMounts utility method in the isMountpoint check and got the same error reliably. Therefore we think checking the mount table is not a good option.

Any thoughts?

cc @AkihiroSuda

@georgethebeatle
Copy link
Copy Markdown
Contributor Author

It's been a while since we last updated this PR. Do you have any thoughts or suggestions? How should we proceed?

rootfs := ""
if len(mounts) > 0 {
rootfs = filepath.Join(r.Bundle, "rootfs")
if err := os.Mkdir(rootfs, 0711); err != nil {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why not 700 or 755?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We just moved that code here from

if err := os.MkdirAll(path, 0711); err != nil {
return nil, err
}
. Not sure why it is 0711 and if it is safe to change it.

@fuweid
Copy link
Copy Markdown
Member

fuweid commented Apr 22, 2019

@danail-branekov @georgethebeatle sorry for the late reply.

In a concurrent scenario where two deletes are running in parallel it may happen that deleteA has checked whether something is a mountpoint and is about to unmount it. In the meanwhile, deleteB might have already umounted the path thus making deleteA unmount to fail (as the path has been already unmounted). In order to avoid the race we unmount straight away and ignore the EINVAL

@danail-branekov mount.UnmountAll has already ignore the EINVAL. I think the case will not be issue, right?

// UnmountAll repeatedly unmounts the given mount point until there
// are no mounts remaining (EINVAL is returned by mount), which is
// useful for undoing a stack of mounts on the same mount point.
func UnmountAll(mount string, flags int) error {
        for {
                if err := unmount(mount, flags); err != nil {
                        // EINVAL is returned if the target is not a
                        // mount point, indicating that we are
                        // done. It can also indicate a few other
                        // things (such as invalid flags) which we
                        // unfortunately end up squelching here too.
                        if err == unix.EINVAL {
                                return nil
                        }
                        return err
                }
        }
}

@georgethebeatle I was thinking we can check the mount table in the shim side. Anyway,
it seems that easy to fix this problem by the empty string. :)

@georgethebeatle
Copy link
Copy Markdown
Contributor Author

georgethebeatle commented Apr 22, 2019

mount.UnmountAll has already ignore the EINVAL. I think the case will not be issue, right?

Not quite. The fact that the mount point check is racy can have 2 effects:

  1. You check the mount table and rootfs is a mount point, then another thread unmounts rootfs, then you try to unmount rootfs which is no longer a mount point. This one is fine as you point out since we are ignoring EINVAL
  2. You check the mount table and rootfs is not there, which means it is not mounted. In the meantime another thread mounts rootfs, but you are already past the check so you do not attempt to unmount which leads to a leaked mount point and an EBUSY error when you later on try to remove rootfs. This is the same problem that we managed to reproduce by running all tests in a loop until they fail using the "check the mount table" implementation. Therefore we think it is better to take the "invalid path" approach where we are telling UnmountAll whether or not to attempt unmount by passing an invalid (or non-existing) path.

@fuweid
Copy link
Copy Markdown
Member

fuweid commented Apr 23, 2019

Hi @georgethebeatle

Yeah. My point is that we can check mount table in the runtime(shim) side, not in the unmount function. The cleanupAfterDeadShim will check the rootfs is mount point or not. Since the task id is deleted after by the runtime.terminate action, we cannot create two same tasks in the same time. I think it can avoid some concurrent issues. But we cannot prevent the manually case. For example, the rootfs is provided and bundle/rootfs is empty folder. Before the cleanup, we can mount the random thing on the rootfs folder to make the cleanup failed.

Empty rootfs is easy to make it happen, I agree. We can do skip-unmount-checker in shim side, not in the unmount side, because we cannot let all the cover things in mount package. it is just my two cents. Thanks

@georgethebeatle
Copy link
Copy Markdown
Contributor Author

@fuweid I feel like we are kind of on the same page but do not understand whether you want us to do some more work. Should we do something more to get this merged and what do you suggest we do?

@georgethebeatle
Copy link
Copy Markdown
Contributor Author

@fuweid About your proposition that we do a mountpoint check in cleanupAfterDeadShim hoping that this would avoid concurrent issues I have several concerns:

  1. As far as I see cleanupAfterDeadShim is called if the shim fails to start or after it exits as an exit handler. Maybe the exit handler is being called late enough to avoid concurrency, but I do not know the codebase well enough to be convinced, so this should be demonstrated by running the tests lots and lots of times without any failures (assuming that cleanupAfterDeadShim does the mountpoint check). As a whole I am not comfortable introducing a racy check and hoping that it will not be a problem.
  2. The cleanupAftertDeadShim is only used in v1 so it is not a universal solution. In v2 containerd delegates to external shim binaries. To my best knowledge the v2 shim binary calls out to UnmountAll to ensure that the mount point is gone. If we have to apply your point to v2 this would mean that each binary would have to do the unmount itself. Not sure if that is desirable

What do you think?

@fuweid
Copy link
Copy Markdown
Member

fuweid commented Apr 27, 2019

Hi @georgethebeatle sorry for the late reply.

cleanupAfterDeadShim is only used in v1 shim. But in the v2 shim, there are two places to handle unmount:

  • If loadTask fails, the v2 task manager will try to load container and unmount rootfs.
  • v2 shim binary will unmount rootfs point during Delete API call.

I have checked your code.

diff --git a/runtime/v2/bundle.go b/runtime/v2/bundle.go
index 7139410..7897a1c 100644
--- a/runtime/v2/bundle.go
+++ b/runtime/v2/bundle.go
@@ -89,10 +89,6 @@ func NewBundle(ctx context.Context, root, state, id string, spec []byte) (b *Bun
                }
        }
        paths = append(paths, work)
-       // create rootfs dir
-       if err := os.Mkdir(filepath.Join(b.Path, "rootfs"), 0711); err != nil {
-               return nil, err
-       }
        // symlink workdir
        if err := os.Symlink(work, filepath.Join(b.Path, "work")); err != nil {
                return nil, err

And tried to run it with v2 shim binary in my local [no idea why the CI is still pass 😂 ]

root@ubuntu-xenial ~/g/s/g/c/containerd# ctr run --runtime io.containerd.runc.v2 --rm docker.io/library/alpine:latest pause date                                          me-cross-push!
ctr: failed to mount rootfs component &{overlay overlay [workdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/49/work upperdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/49/fs lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs]}: no such file or directory: unknown

root@ubuntu-xenial ~/g/s/g/c/containerd# ctr run --rm docker.io/library/alpine:latest pause date                                                                        1 me-cross-push!
Sat Apr 27 15:48:47 UTC 2019

I think we should not just skip the rootfs in v2. Just random thought: record the information about the provided rootfs in file(read later) or add empty checker in everywhere.

BTW, Do you consider to use v2 shim in your production? It seems that we can handle the unmount thing in custom shim binary.

@georgethebeatle
Copy link
Copy Markdown
Contributor Author

Hi @fuweid

On a local environment with the PR changes we were able to successfully execute the following command:

ctr run --runtime io.containerd.runc.v2 --rm docker.io/library/alpine:latest test echo hi

We were able to get the error you are talking about by applying the diff you gave above to master (which is only a small part of the PR), but could you explain exactly what you did?

Apart from that we experimented with what you suggested: we moved the UnmountAll call to the exitHandler of the shim (in v1)
and made UnmountAll check whether the path is a mountpoint and unmount only when it is:

diff --git a/runtime/v1/linux/runtime.go b/runtime/v1/linux/runtime.go
index 545e2167..c2f889d3 100644
--- a/runtime/v1/linux/runtime.go
+++ b/runtime/v1/linux/runtime.go
@@ -190,12 +190,17 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
      cgroup = v.(*runctypes.CreateOptions).ShimCgroup
    }
    exitHandler := func() {
+     log.G(ctx).WithField("id", id).Info("short cirquiting")
+     if err2 := mount.UnmountAll(filepath.Join(bundle.path, "rootfs"), 0); err2 != nil {
+       log.G(ctx).WithError(err2).Warn("Failed to cleanup rootfs mount")
+     }
      log.G(ctx).WithField("id", id).Info("shim reaped")
      t, err := r.tasks.Get(ctx, id)

Unfortunately the tests started failing with the same error as mentioned above: #3148 (comment):

 Mount table is
 sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
 proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
 udev /dev devtmpfs rw,nosuid,relatime,size=1007456k,nr_inodes=251864,mode=755 0 0
 devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
 tmpfs /run tmpfs rw,nosuid,noexec,relatime,size=204060k,mode=755 0 0
 /dev/sda1 / ext4 rw,relatime 0 0
 securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
 tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0
 tmpfs /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0
 tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0
 cgroup2 /sys/fs/cgroup/unified cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate 0 0
 cgroup /sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,xattr,name=systemd 0 0
 pstore /sys/fs/pstore pstore rw,nosuid,nodev,noexec,relatime 0 0
 bpf /sys/fs/bpf bpf rw,nosuid,nodev,noexec,relatime,mode=700 0 0
 cgroup /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0
 cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0
 cgroup /sys/fs/cgroup/pids cgroup rw,nosuid,nodev,noexec,relatime,pids 0 0
 cgroup /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0
 cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0
 cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0
 cgroup /sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0
 cgroup /sys/fs/cgroup/rdma cgroup rw,nosuid,nodev,noexec,relatime,rdma 0 0
 cgroup /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0
 cgroup /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0
 cgroup /sys/fs/cgroup/hugetlb cgroup rw,nosuid,nodev,noexec,relatime,hugetlb 0 0
 debugfs /sys/kernel/debug debugfs rw,relatime 0 0
 mqueue /dev/mqueue mqueue rw,relatime 0 0
 hugetlbfs /dev/hugepages hugetlbfs rw,relatime,pagesize=2M 0 0
 fusectl /sys/fs/fuse/connections fusectl rw,relatime 0 0
 configfs /sys/kernel/config configfs rw,relatime 0 0
 /dev/loop0 /snap/core/6673 squashfs ro,nodev,relatime 0 0
 /dev/loop1 /snap/lxd/10601 squashfs ro,nodev,relatime 0 0
 tmpfs /run/snapd/ns tmpfs rw,nosuid,noexec,relatime,size=204060k,mode=755 0 0
 nsfs /run/snapd/ns/lxd.mnt nsfs rw 0 0
 vagrant /vagrant vboxsf rw,nodev,relatime,uid=1000,gid=1000,ttl=0,dmode=037777777777,fmode=037777777777,dmask=00,fmask=00 0 0
 vagrant_data /vagrant_data vboxsf rw,nodev,relatime,uid=1000,gid=1000,ttl=0,dmode=037777777777,fmode=037777777777,dmask=00,fmask=00 0 0
 /dev/loop2 /snap/core/6818 squashfs ro,nodev,relatime 0 0
 tmpfs /run/user/1000 tmpfs rw,nosuid,nodev,relatime,size=204056k,mode=700,uid=1000,gid=1000 0 0
 tracefs /sys/kernel/debug/tracing tracefs rw,relatime 0 0
 overlay /tmp/containerd-test-new-daemon-with-config143921677/state/io.containerd.runtime.v2.task/testing/.TestDaemonRuntimeRoot/rootfs overlay rw,relatime,lowerdir=/tmp/containerd-test-new-daemon-with-config143921677/root/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/tmp/containerd-test-new-daemon-with-config143921677/root/io.containerd.snapshotter.v1.overlayfs/snapshots/2/fs,workdir=/tmp/containerd-test-new-daemon-with-config143921677/root/io.containerd.snapshotter.v1.overlayfs/snapshots/2/work,xino=off 0 0
 overlay /tmp/containerd-test-new-daemon-with-config095602708/state/io.containerd.runtime.v2.task/testing/.TestDaemonRuntimeRoot/rootfs overlay rw,relatime,lowerdir=/tmp/containerd-test-new-daemon-with-config095602708/root/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/tmp/containerd-test-new-daemon-with-config095602708/root/io.containerd.snapshotter.v1.overlayfs/snapshots/2/fs,workdir=/tmp/containerd-test-new-daemon-with-config095602708/root/io.containerd.snapshotter.v1.overlayfs/snapshots/2/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/.TestDeleteContainerExecCreated/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/224/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/224/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/.TestContainerAttach/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/236/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/236/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/.TestContainerPTY/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/238/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/238/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/.TestContainerUser_UserIDAndGroupName/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/240/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/240/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/TestBindLowPortNonOpt/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/244/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/244/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/.TestTaskResize/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/248/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/248/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v2.task/testing/.TestContainerRuntimeOptionsv2/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/250/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/250/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/TestDeleteRunningContainer/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/253/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/253/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/TestContainerKill/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/254/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/254/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/.TestContainerUser_UserIDAndGroupID/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/256/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/256/work,xino=off 0 0
 overlay /run/containerd-test/io.containerd.runtime.v1.linux/testing/TestProcessForceDelete/rootfs overlay rw,relatime,lowerdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/255/fs,workdir=/var/lib/containerd-test/io.containerd.snapshotter.v1.overlayfs/snapshots/255/work,xino=off 0 0


 Skipping unmount of  /run/containerd-test/io.containerd.runtime.v1.linux/testing/TestContainerUser_UserIDAndGroupID/rootfs

This time the race would happen every time we run the tests, rather than once in 80 runs, so it seems to have gotten worse.

We are still reluctant to introduce a racey check. Do you have any further thoughts?

cc @AkihiroSuda what do you think?

@fuweid
Copy link
Copy Markdown
Member

fuweid commented May 7, 2019

Hi @georgethebeatle I just applied the change in master and run a container.

step 1: clean up

root@ubuntu-xenial ~/g/s/g/c/containerd# service containerd stop                                                                                                                                        me-testing!
root@ubuntu-xenial ~/g/s/g/c/containerd# rm -rf /var/lib/containerd                                                                                                                                     me-testing!
root@ubuntu-xenial ~/g/s/g/c/containerd# ls -al /var/lib/containerd                                                                                                                                     me-testing!
ls: cannot access '/var/lib/containerd': No such file or directory
root@ubuntu-xenial ~/g/s/g/c/containerd# rm -rf /var/run/containerd/runc                                                                                                                              2 me-testing!
root@ubuntu-xenial ~/g/s/g/c/containerd# ls -al /var/run/containerd/runc                                                                                                                                me-testing!
ls: cannot access '/var/run/containerd/runc': No such file or directory
root@ubuntu-xenial ~/g/s/g/c/containerd# git --no-pager diff                                                                                                                                          2 me-testing!
diff --git a/runtime/v2/bundle.go b/runtime/v2/bundle.go
index 7139410..51f0fa6 100644
--- a/runtime/v2/bundle.go
+++ b/runtime/v2/bundle.go
@@ -89,10 +89,13 @@ func NewBundle(ctx context.Context, root, state, id string, spec []byte) (b *Bun
                }
        }
        paths = append(paths, work)
-       // create rootfs dir
-       if err := os.Mkdir(filepath.Join(b.Path, "rootfs"), 0711); err != nil {
-               return nil, err
-       }
+       /*
+               // create rootfs dir
+               if err := os.Mkdir(filepath.Join(b.Path, "rootfs"), 0711); err != nil {
+                       return nil, err
+               }
+       */
+
        // symlink workdir
        if err := os.Symlink(work, filepath.Join(b.Path, "work")); err != nil {
                return nil, err

step2: make and restart

root@ubuntu-xenial ~/g/s/g/c/containerd# make && make install                                                                                                                                           me-testing!
+ bin/ctr
+ bin/containerd
+ bin/containerd-stress
+ bin/containerd-shim
+ bin/containerd-shim-runc-v1
+ bin/containerd-shim-runc-v2
+ binaries
+ install bin/ctr bin/containerd bin/containerd-stress bin/containerd-shim bin/containerd-shim-runc-v1 bin/containerd-shim-runc-v2
root@ubuntu-xenial ~/g/s/g/c/containerd# service containerd start

step3: pull image and run it with runtime v2 API

root@ubuntu-xenial ~/g/s/g/c/containerd# ctr image pull registry.hub.docker.com/library/busybox:1.25                                                                                                    me-testing!
registry.hub.docker.com/library/busybox:1.25:                                     resolved       |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:29f5d56d12684887bdfa50dcd29fc31eea4aaf4ad3bec43daf19026a7ce69912: done           |++++++++++++++++++++++++++++++++++++++|
layer-sha256:56bec22e355981d8ba0878c6c2f23b21f422f30ab0aba188b54f1ffeff59c190:    done           |++++++++++++++++++++++++++++++++++++++|
config-sha256:e02e811dd08fd49e7f6032625495118e63f597eb150403d02e3238af1df240ba:   done           |++++++++++++++++++++++++++++++++++++++|
elapsed: 10.8s                                                                    total:  640.6  (59.3 KiB/s)
unpacking linux/amd64 sha256:29f5d56d12684887bdfa50dcd29fc31eea4aaf4ad3bec43daf19026a7ce69912...
done
root@ubuntu-xenial ~/g/s/g/c/containerd# ctr run --runtime io.containerd.runc.v2 --rm registry.hub.docker.com/library/busybox:1.25 test echo hi                                                         me-testing!
ctr: ttrpc: client shutting down: ttrpc: closed: unknown
root@ubuntu-xenial ~/g/s/g/c/containerd# journalctl -u containerd.service -f                                                                                                                          1 me-testing!
-- Logs begin at Tue 2019-04-30 14:39:00 CST. --
May 07 15:56:40 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:40.828676376+08:00" level=debug msg="get snapshot mounts" key=test
May 07 15:56:40 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:40.837119389+08:00" level=info msg="starting signal loop" namespace=default path=/run/containerd/io.containerd.runtime.v2.task/default/test
pid=13146
May 07 15:56:40 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:40.974091909+08:00" level=error msg="not found"
May 07 15:56:40 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:40.976926410+08:00" level=debug msg="remove snapshot" key=test
May 07 15:56:40 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:40.977740021+08:00" level=debug msg="event published" ns=default topic="/snapshot/remove" type=containerd.events.SnapshotRemove
May 07 15:56:40 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:40.978615668+08:00" level=debug msg="event published" ns=default topic="/containers/delete" type=containerd.events.ContainerDelete
May 07 15:56:41 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:41.015853110+08:00" level=debug msg="schedule snapshotter cleanup" snapshotter=overlayfs
May 07 15:56:41 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:41.016691814+08:00" level=debug msg="removed snapshot" key="default/3/test" snapshotter=overlayfs
May 07 15:56:41 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:41.017067204+08:00" level=debug msg="snapshot garbage collected" d=941.24µs snapshotter=overlayfs
May 07 15:56:41 ubuntu-xenial containerd[12728]: time="2019-05-07T15:56:41.017231622+08:00" level=debug msg="garbage collected" d=803.161µs

@georgethebeatle I think we can give up the mount check idea 😂 Focus on your original idea.

I miss the part about create rootfs in shim side. you are right, I think we should create rootfs in shim side. If there is provided rootfs, we should not create the folder.

It seems that check empty/non-exist folder in mount.Unmount is the way to reduce duplicate check in code base.

LGTM. Sorry for taking it so long.

But I would like to invite @dmcgowan to check the mount part. :)

Copy link
Copy Markdown
Member

@fuweid fuweid left a comment

Choose a reason for hiding this comment

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

LGTM

need @dmcgowan to check the mount part. Thanks

@crosbymichael
Copy link
Copy Markdown
Member

LGTM

@crosbymichael crosbymichael merged commit 19af235 into containerd:master May 7, 2019
@georgethebeatle
Copy link
Copy Markdown
Contributor Author

Hey @crosbymichael @dmcgowan

I have question regarding this PR. It was merged to master in between 1.2.6 and 1.2.7 releases, but it is not available in 1.2.7. Why is that so? When will it be released and should we do something more so that it gets in the next release?

@thaJeztah
Copy link
Copy Markdown
Member

@georgethebeatle the master branch is used to work on containerd 1.3.0; there's release branches for the 1.0, 1.1.x and 1.2.x releases; to include something in one of those releases, fixes have to be back ported / cherry-picked (e.g. see https://github.com/containerd/containerd/pulls?q=is%3Apr+release+in%3Atitle+is%3Aclosed)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants