btrfs: auto-detect device
No longer need to set `plugins.snapshot-btrfs.device` manually Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
parent
e37c337a3b
commit
d5707d3ac7
@ -7,51 +7,69 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/containerd/btrfs"
|
"github.com/containerd/btrfs"
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/containerd/mountinfo"
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
"github.com/containerd/containerd/snapshot"
|
"github.com/containerd/containerd/snapshot"
|
||||||
"github.com/containerd/containerd/snapshot/storage"
|
"github.com/containerd/containerd/snapshot/storage"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type btrfsConfig struct {
|
|
||||||
Device string `toml:"device"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
plugin.Register("snapshot-btrfs", &plugin.Registration{
|
plugin.Register("snapshot-btrfs", &plugin.Registration{
|
||||||
Type: plugin.SnapshotPlugin,
|
Type: plugin.SnapshotPlugin,
|
||||||
Config: &btrfsConfig{},
|
|
||||||
Init: func(ic *plugin.InitContext) (interface{}, error) {
|
Init: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
root := filepath.Join(ic.Root, "snapshot", "btrfs")
|
root := filepath.Join(ic.Root, "snapshot", "btrfs")
|
||||||
conf := ic.Config.(*btrfsConfig)
|
return NewSnapshotter(root)
|
||||||
if conf.Device == "" {
|
|
||||||
// TODO: check device for root
|
|
||||||
return nil, errors.Errorf("btrfs requires \"device\" configuration")
|
|
||||||
}
|
|
||||||
return NewSnapshotter(conf.Device, root)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type snapshotter struct {
|
type snapshotter struct {
|
||||||
device string // maybe we can resolve it with path?
|
device string // device of the root
|
||||||
root string // root provides paths for internal storage.
|
root string // root provides paths for internal storage.
|
||||||
ms *storage.MetaStore
|
ms *storage.MetaStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getBtrfsDevice(root string, mounts []*mountinfo.Info) (string, error) {
|
||||||
|
device := ""
|
||||||
|
deviceMountpoint := ""
|
||||||
|
for _, info := range mounts {
|
||||||
|
if (info.Root == "/" || info.Root == "") && strings.HasPrefix(root, info.Mountpoint) {
|
||||||
|
if info.Fstype == "btrfs" && len(info.Mountpoint) > len(deviceMountpoint) {
|
||||||
|
device = info.Source
|
||||||
|
deviceMountpoint = info.Mountpoint
|
||||||
|
}
|
||||||
|
if root == info.Mountpoint && info.Fstype != "btrfs" {
|
||||||
|
return "", fmt.Errorf("%s needs to be btrfs, but seems %s", root, info.Fstype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if device == "" {
|
||||||
|
// TODO: automatically mount loopback device here?
|
||||||
|
return "", fmt.Errorf("%s is not mounted as btrfs", root)
|
||||||
|
}
|
||||||
|
return device, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewSnapshotter returns a Snapshotter using btrfs. Uses the provided
|
// NewSnapshotter returns a Snapshotter using btrfs. Uses the provided
|
||||||
// device and root directory for snapshots and stores the metadata in
|
// root directory for snapshots and stores the metadata in
|
||||||
// a file in the provided root.
|
// a file in the provided root.
|
||||||
func NewSnapshotter(device, root string) (snapshot.Snapshotter, error) {
|
// root needs to be a mount point of btrfs.
|
||||||
if err := os.MkdirAll(root, 0700); err != nil {
|
func NewSnapshotter(root string) (snapshot.Snapshotter, error) {
|
||||||
|
mounts, err := mountinfo.GetMounts()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
device, err := getBtrfsDevice(root, mounts)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
active = filepath.Join(root, "active")
|
active = filepath.Join(root, "active")
|
||||||
snapshots = filepath.Join(root, "snapshots")
|
snapshots = filepath.Join(root, "snapshots")
|
||||||
@ -193,7 +211,7 @@ func (b *snapshotter) mounts(dir string) ([]containerd.Mount, error) {
|
|||||||
return []containerd.Mount{
|
return []containerd.Mount{
|
||||||
{
|
{
|
||||||
Type: "btrfs",
|
Type: "btrfs",
|
||||||
Source: b.device, // device?
|
Source: b.device,
|
||||||
// NOTE(stevvooe): While it would be nice to use to uuids for
|
// NOTE(stevvooe): While it would be nice to use to uuids for
|
||||||
// mounts, they don't work reliably if the uuids are missing.
|
// mounts, they don't work reliably if the uuids are missing.
|
||||||
Options: options,
|
Options: options,
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
|
"github.com/containerd/containerd/mountinfo"
|
||||||
"github.com/containerd/containerd/snapshot"
|
"github.com/containerd/containerd/snapshot"
|
||||||
"github.com/containerd/containerd/snapshot/testsuite"
|
"github.com/containerd/containerd/snapshot/testsuite"
|
||||||
"github.com/containerd/containerd/testutil"
|
"github.com/containerd/containerd/testutil"
|
||||||
@ -24,7 +25,7 @@ const (
|
|||||||
func boltSnapshotter(t *testing.T) func(context.Context, string) (snapshot.Snapshotter, func(), error) {
|
func boltSnapshotter(t *testing.T) func(context.Context, string) (snapshot.Snapshotter, func(), error) {
|
||||||
return func(ctx context.Context, root string) (snapshot.Snapshotter, func(), error) {
|
return func(ctx context.Context, root string) (snapshot.Snapshotter, func(), error) {
|
||||||
device := setupBtrfsLoopbackDevice(t, root)
|
device := setupBtrfsLoopbackDevice(t, root)
|
||||||
snapshotter, err := NewSnapshotter(device.deviceName, root)
|
snapshotter, err := NewSnapshotter(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -215,3 +216,64 @@ func (device *testDevice) remove(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetBtrfsDevice(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
expectedDevice string
|
||||||
|
expectedError string
|
||||||
|
root string
|
||||||
|
mounts []*mountinfo.Info
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
expectedDevice: "/dev/loop0",
|
||||||
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
|
mounts: []*mountinfo.Info{
|
||||||
|
{Root: "/", Mountpoint: "/", Fstype: "ext4", Source: "/dev/sda1"},
|
||||||
|
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", Fstype: "btrfs", Source: "/dev/loop0"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expectedError: "/var/lib/containerd/snapshot/btrfs is not mounted as btrfs",
|
||||||
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
|
mounts: []*mountinfo.Info{
|
||||||
|
{Root: "/", Mountpoint: "/", Fstype: "ext4", Source: "/dev/sda1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expectedDevice: "/dev/sda1",
|
||||||
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
|
mounts: []*mountinfo.Info{
|
||||||
|
{Root: "/", Mountpoint: "/", Fstype: "btrfs", Source: "/dev/sda1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expectedDevice: "/dev/sda2",
|
||||||
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
|
mounts: []*mountinfo.Info{
|
||||||
|
{Root: "/", Mountpoint: "/", Fstype: "btrfs", Source: "/dev/sda1"},
|
||||||
|
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", Fstype: "btrfs", Source: "/dev/sda2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expectedDevice: "/dev/sda2",
|
||||||
|
root: "/var/lib/containerd/snapshot/btrfs",
|
||||||
|
mounts: []*mountinfo.Info{
|
||||||
|
{Root: "/", Mountpoint: "/var/lib/containerd/snapshot/btrfs", Fstype: "btrfs", Source: "/dev/sda2"},
|
||||||
|
{Root: "/", Mountpoint: "/var/lib/foooooooooooooooooooo/baaaaaaaaaaaaaaaaaaaar", Fstype: "btrfs", Source: "/dev/sda3"}, // mountpoint length longer than /var/lib/containerd/snapshot/btrfs
|
||||||
|
{Root: "/", Mountpoint: "/", Fstype: "btrfs", Source: "/dev/sda1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tc := range testCases {
|
||||||
|
device, err := getBtrfsDevice(tc.root, tc.mounts)
|
||||||
|
if err != nil && tc.expectedError == "" {
|
||||||
|
t.Fatalf("%d: expected nil, got %v", i, err)
|
||||||
|
}
|
||||||
|
if err != nil && !strings.Contains(err.Error(), tc.expectedError) {
|
||||||
|
t.Fatalf("%d: expected %s, got %v", i, tc.expectedError, err)
|
||||||
|
}
|
||||||
|
if err == nil && device != tc.expectedDevice {
|
||||||
|
t.Fatalf("%d: expected %s, got %s", i, tc.expectedDevice, device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user