diff --git a/daemon/graphdriver/graphtest/graphtest_unix.go b/daemon/graphdriver/graphtest/graphtest_unix.go index df8d08879e..85342abba1 100644 --- a/daemon/graphdriver/graphtest/graphtest_unix.go +++ b/daemon/graphdriver/graphtest/graphtest_unix.go @@ -5,12 +5,16 @@ package graphtest import ( "fmt" "io/ioutil" + "math/rand" "os" "path" + "reflect" "syscall" "testing" + "unsafe" "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/go-units" ) var ( @@ -301,3 +305,45 @@ func DriverTestCreateSnap(t *testing.T, drivername string) { verifyBase(t, driver, "Snap") } + +func writeRandomFile(path string, size uint64) error { + buf := make([]int64, size/8) + + r := rand.NewSource(0) + for i := range buf { + buf[i] = r.Int63() + } + + // Cast to []byte + header := *(*reflect.SliceHeader)(unsafe.Pointer(&buf)) + header.Len *= 8 + header.Cap *= 8 + data := *(*[]byte)(unsafe.Pointer(&header)) + + return ioutil.WriteFile(path, data, 0700) +} + +// DriverTestSetQuota Create a driver and test setting quota. +func DriverTestSetQuota(t *testing.T, drivername string) { + driver := GetDriver(t, drivername) + defer PutDriver(t) + + createBase(t, driver, "Base") + storageOpt := make(map[string]string, 1) + storageOpt["size"] = "50M" + if err := driver.Create("zfsTest", "Base", "", storageOpt); err != nil { + t.Fatal(err) + } + + mountPath, err := driver.Get("zfsTest", "") + if err != nil { + t.Fatal(err) + } + + quota := uint64(50 * units.MiB) + err = writeRandomFile(path.Join(mountPath, "file"), quota*2) + if pathError, ok := err.(*os.PathError); ok && pathError.Err != syscall.EDQUOT { + t.Fatalf("expect write() to fail with %v, got %v", syscall.EDQUOT, err) + } + +} diff --git a/daemon/graphdriver/zfs/zfs.go b/daemon/graphdriver/zfs/zfs.go index 3b41ff8b48..45e2309eb1 100644 --- a/daemon/graphdriver/zfs/zfs.go +++ b/daemon/graphdriver/zfs/zfs.go @@ -250,11 +250,7 @@ func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[s // Create prepares the dataset and filesystem for the ZFS driver for the given id under the parent. func (d *Driver) Create(id string, parent string, mountLabel string, storageOpt map[string]string) error { - if len(storageOpt) != 0 { - return fmt.Errorf("--storage-opt is not supported for zfs") - } - - err := d.create(id, parent) + err := d.create(id, parent, storageOpt) if err == nil { return nil } @@ -273,22 +269,58 @@ func (d *Driver) Create(id string, parent string, mountLabel string, storageOpt } // retry - return d.create(id, parent) + return d.create(id, parent, storageOpt) } -func (d *Driver) create(id, parent string) error { +func (d *Driver) create(id, parent string, storageOpt map[string]string) error { name := d.zfsPath(id) + quota, err := parseStorageOpt(storageOpt) + if err != nil { + return err + } if parent == "" { mountoptions := map[string]string{"mountpoint": "legacy"} fs, err := zfs.CreateFilesystem(name, mountoptions) if err == nil { - d.Lock() - d.filesystemsCache[fs.Name] = true - d.Unlock() + err = setQuota(name, quota) + if err == nil { + d.Lock() + d.filesystemsCache[fs.Name] = true + d.Unlock() + } } return err } - return d.cloneFilesystem(name, d.zfsPath(parent)) + err = d.cloneFilesystem(name, d.zfsPath(parent)) + if err == nil { + err = setQuota(name, quota) + } + return err +} + +func parseStorageOpt(storageOpt map[string]string) (string, error) { + // Read size to change the disk quota per container + for k, v := range storageOpt { + key := strings.ToLower(k) + switch key { + case "size": + return v, nil + default: + return "0", fmt.Errorf("Unknown option %s", key) + } + } + return "0", nil +} + +func setQuota(name string, quota string) error { + if quota == "0" { + return nil + } + fs, err := zfs.GetDataset(name) + if err != nil { + return err + } + return fs.SetProperty("quota", quota) } // Remove deletes the dataset, filesystem and the cache for the given id. diff --git a/daemon/graphdriver/zfs/zfs_test.go b/daemon/graphdriver/zfs/zfs_test.go index 0e7937eccb..3e22928438 100644 --- a/daemon/graphdriver/zfs/zfs_test.go +++ b/daemon/graphdriver/zfs/zfs_test.go @@ -26,6 +26,10 @@ func TestZfsCreateSnap(t *testing.T) { graphtest.DriverTestCreateSnap(t, "zfs") } +func TestZfsSetQuota(t *testing.T) { + graphtest.DriverTestSetQuota(t, "zfs") +} + func TestZfsTeardown(t *testing.T) { graphtest.PutDriver(t) }