Skip to content

Commit 5bd3ca9

Browse files
committed
feature: support take over moby container
Signed-off-by: Michael Wan <[email protected]>
1 parent 6f72589 commit 5bd3ca9

File tree

6 files changed

+155
-15
lines changed

6 files changed

+155
-15
lines changed

ctrd/container.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -357,23 +357,30 @@ func (c *Client) createContainer(ctx context.Context, ref, id string, container
357357
logrus.Infof("success to get image %s, container id %s", img.Name(), id)
358358

359359
// create container
360-
specOptions := []oci.SpecOpts{
361-
oci.WithRootFSPath("rootfs"),
362-
}
363-
364360
options := []containerd.NewContainerOpts{
365-
containerd.WithSpec(container.Spec, specOptions...),
366361
containerd.WithRuntime(fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS), &runctypes.RuncOptions{
367362
Runtime: container.Runtime,
368363
RuntimeRoot: runtimeRoot,
369364
}),
370365
}
371366

372-
// check snapshot exist or not.
373-
if _, err := c.GetSnapshot(ctx, id); err != nil {
374-
return errors.Wrapf(err, "failed to create container %s", id)
367+
rootFSPath := "rootfs"
368+
// if container is taken over by pouch, not created by pouch
369+
if container.Takeover {
370+
rootFSPath = container.BaseFS
371+
} else { // containers created by pouch must first create snapshot
372+
// check snapshot exist or not.
373+
if _, err := c.GetSnapshot(ctx, id); err != nil {
374+
return errors.Wrapf(err, "failed to create container %s", id)
375+
}
376+
options = append(options, containerd.WithSnapshot(id))
377+
}
378+
379+
// specify Spec for new container
380+
specOptions := []oci.SpecOpts{
381+
oci.WithRootFSPath(rootFSPath),
375382
}
376-
options = append(options, containerd.WithSnapshot(id))
383+
options = append(options, containerd.WithSpec(container.Spec, specOptions...))
377384

378385
nc, err := wrapperCli.client.NewContainer(ctx, id, options...)
379386
if err != nil {

ctrd/container_types.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,23 @@ import (
66
specs "github.com/opencontainers/runtime-spec/specs-go"
77
)
88

9-
// Container wraps container's info.
9+
// Container wraps container's info. there have two kind of containers now:
10+
// One is created by pouch, no need any different operations,
11+
// The other is just taken over by pouch, like created by moby, we use `Takeover` flag to mark it,
12+
// because the container is not created by pouch, so we must specify rootfs the container used, so that
13+
// we can use the rootfs to create new containerd container.
1014
type Container struct {
1115
ID string
1216
Image string
1317
Runtime string
1418
IO *containerio.IO
1519
Spec *specs.Spec
20+
21+
// BaseFS is rootfs used by containerd container
22+
BaseFS string
23+
// Takeover specify if the containerd container is just
24+
// taken over by pouch, not created by pouch
25+
Takeover bool
1626
}
1727

1828
// Process wraps exec process's info.

daemon/mgr/container.go

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ import (
2626
"github.com/alibaba/pouch/pkg/collect"
2727
"github.com/alibaba/pouch/pkg/errtypes"
2828
"github.com/alibaba/pouch/pkg/meta"
29+
mountutils "github.com/alibaba/pouch/pkg/mount"
2930
"github.com/alibaba/pouch/pkg/randomid"
3031
"github.com/alibaba/pouch/pkg/utils"
3132
"github.com/alibaba/pouch/storage/quota"
3233
volumetypes "github.com/alibaba/pouch/storage/volume/types"
3334

3435
"github.com/containerd/containerd/errdefs"
36+
"github.com/containerd/containerd/mount"
3537
"github.com/containerd/containerd/namespaces"
3638
"github.com/docker/libnetwork"
3739
"github.com/go-openapi/strfmt"
@@ -571,14 +573,24 @@ func (mgr *ContainerManager) createContainerdContainer(ctx context.Context, c *C
571573

572574
c.Lock()
573575
ctrdContainer := &ctrd.Container{
574-
ID: c.ID,
575-
Image: c.Config.Image,
576-
Runtime: c.HostConfig.Runtime,
577-
Spec: sw.s,
578-
IO: io,
576+
ID: c.ID,
577+
Image: c.Config.Image,
578+
Runtime: c.HostConfig.Runtime,
579+
Spec: sw.s,
580+
IO: io,
581+
Takeover: c.Takeover,
582+
BaseFS: c.BaseFS,
579583
}
580584
c.Unlock()
581585

586+
// if container is taken over by pouch,
587+
// verify BaseFS before create containerd container
588+
if c.Takeover {
589+
if err := mgr.ensureRootFSMounted(c.BaseFS, c.Snapshotter.Data); err != nil {
590+
return fmt.Errorf("failed to mount container rootfs: %v", err)
591+
}
592+
}
593+
582594
if err := mgr.Client.CreateContainer(ctx, ctrdContainer); err != nil {
583595
logrus.Errorf("failed to create new containerd container: %v", err)
584596

@@ -605,6 +617,53 @@ func (mgr *ContainerManager) createContainerdContainer(ctx context.Context, c *C
605617
return c.Write(mgr.Store)
606618
}
607619

620+
func (mgr *ContainerManager) ensureRootFSMounted(rootfs string, snapData map[string]string) error {
621+
if rootfs == "" || len(snapData) == 0 {
622+
return fmt.Errorf("container rootfs or snapshotter data is empty")
623+
}
624+
625+
// check if rootfs already mounted
626+
notMounted, err := mountutils.IsLikelyNotMountPoint(rootfs)
627+
if err != nil {
628+
return err
629+
}
630+
// rootfs already mounted
631+
if !notMounted {
632+
return nil
633+
}
634+
635+
var workDir, upperDir, lowerDir string
636+
for _, dir := range []string{"WorkDir", "UpperDir", "LowerDir"} {
637+
if v, ok := snapData[dir]; ok {
638+
switch dir {
639+
case "WorkDir":
640+
workDir = v
641+
case "UpperDir":
642+
upperDir = v
643+
case "LowerDir":
644+
lowerDir = v
645+
}
646+
}
647+
}
648+
649+
if workDir == "" || upperDir == "" || lowerDir == "" {
650+
return fmt.Errorf("faile to mount overlay: one or more dirs in WorkDir, UpperDir and LowerDir are empty")
651+
}
652+
653+
options := []string{
654+
fmt.Sprintf("workdir=%s", snapData["WorkDir"]),
655+
fmt.Sprintf("upperdir=%s", snapData["UpperDir"]),
656+
fmt.Sprintf("lowerdir=%s", snapData["LowerDir"]),
657+
}
658+
mount := mount.Mount{
659+
Type: "overlay",
660+
Source: "overlay",
661+
Options: options,
662+
}
663+
664+
return mount.Mount(rootfs)
665+
}
666+
608667
// Stop stops a running container.
609668
func (mgr *ContainerManager) Stop(ctx context.Context, name string, timeout int64) error {
610669
c, err := mgr.container(name)

daemon/mgr/container_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ type Container struct {
180180

181181
// Escape keys for detach
182182
DetachKeys string
183+
184+
// Specify whether the container is being taken over,
185+
// not created by pouch
186+
Takeover bool
183187
}
184188

185189
// Key returns container's id.

pkg/mount/mount.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package mount
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"syscall"
8+
)
9+
10+
// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
11+
// It is fast but not necessarily ALWAYS correct. If the path is in fact
12+
// a bind mount from one part of a mount to another it will not be detected.
13+
// mkdir /tmp/a /tmp/b; mount --bin /tmp/a /tmp/b; IsLikelyNotMountPoint("/tmp/b")
14+
// will return true. When in fact /tmp/b is a mount point. If this situation
15+
// if of interest to you, don't use this function...
16+
func IsLikelyNotMountPoint(file string) (bool, error) {
17+
stat, err := os.Stat(file)
18+
if err != nil {
19+
return true, err
20+
}
21+
rootStat, err := os.Lstat(filepath.Dir(strings.TrimSuffix(file, "/")))
22+
if err != nil {
23+
return true, err
24+
}
25+
// If the directory has a different device as parent, then it is a mountpoint.
26+
if stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev {
27+
return false, nil
28+
}
29+
30+
return true, nil
31+
}

pkg/mount/mount_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package mount
2+
3+
import "testing"
4+
5+
func TestIsLikelyNotMountPoint(t *testing.T) {
6+
type args struct {
7+
file string
8+
}
9+
tests := []struct {
10+
name string
11+
args args
12+
want bool
13+
wantErr bool
14+
}{
15+
// TODO: Add test cases.
16+
}
17+
for _, tt := range tests {
18+
t.Run(tt.name, func(t *testing.T) {
19+
got, err := IsLikelyNotMountPoint(tt.args.file)
20+
if (err != nil) != tt.wantErr {
21+
t.Errorf("IsLikelyNotMountPoint() error = %v, wantErr %v", err, tt.wantErr)
22+
return
23+
}
24+
if got != tt.want {
25+
t.Errorf("IsLikelyNotMountPoint() = %v, want %v", got, tt.want)
26+
}
27+
})
28+
}
29+
}

0 commit comments

Comments
 (0)