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"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/containerd/btrfs"
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/mountinfo"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/snapshot"
|
||||
"github.com/containerd/containerd/snapshot/storage"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type btrfsConfig struct {
|
||||
Device string `toml:"device"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugin.Register("snapshot-btrfs", &plugin.Registration{
|
||||
Type: plugin.SnapshotPlugin,
|
||||
Config: &btrfsConfig{},
|
||||
Init: func(ic *plugin.InitContext) (interface{}, error) {
|
||||
root := filepath.Join(ic.Root, "snapshot", "btrfs")
|
||||
conf := ic.Config.(*btrfsConfig)
|
||||
if conf.Device == "" {
|
||||
// TODO: check device for root
|
||||
return nil, errors.Errorf("btrfs requires \"device\" configuration")
|
||||
}
|
||||
return NewSnapshotter(conf.Device, root)
|
||||
return NewSnapshotter(root)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
// 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.
|
||||
func NewSnapshotter(device, root string) (snapshot.Snapshotter, error) {
|
||||
if err := os.MkdirAll(root, 0700); err != nil {
|
||||
// root needs to be a mount point of btrfs.
|
||||
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
|
||||
}
|
||||
|
||||
var (
|
||||
active = filepath.Join(root, "active")
|
||||
snapshots = filepath.Join(root, "snapshots")
|
||||
@ -193,7 +211,7 @@ func (b *snapshotter) mounts(dir string) ([]containerd.Mount, error) {
|
||||
return []containerd.Mount{
|
||||
{
|
||||
Type: "btrfs",
|
||||
Source: b.device, // device?
|
||||
Source: b.device,
|
||||
// NOTE(stevvooe): While it would be nice to use to uuids for
|
||||
// mounts, they don't work reliably if the uuids are missing.
|
||||
Options: options,
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/mountinfo"
|
||||
"github.com/containerd/containerd/snapshot"
|
||||
"github.com/containerd/containerd/snapshot/testsuite"
|
||||
"github.com/containerd/containerd/testutil"
|
||||
@ -24,7 +25,7 @@ const (
|
||||
func boltSnapshotter(t *testing.T) func(context.Context, string) (snapshot.Snapshotter, func(), error) {
|
||||
return func(ctx context.Context, root string) (snapshot.Snapshotter, func(), error) {
|
||||
device := setupBtrfsLoopbackDevice(t, root)
|
||||
snapshotter, err := NewSnapshotter(device.deviceName, root)
|
||||
snapshotter, err := NewSnapshotter(root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -215,3 +216,64 @@ func (device *testDevice) remove(t *testing.T) {
|
||||
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