Skip to content

Commit

Permalink
Merge pull request #21946 from chosenken/add_disk_quota_to_zfs
Browse files Browse the repository at this point in the history
Add support for setting storage size on ZFS containers
  • Loading branch information
cpuguy83 committed Jun 8, 2016
2 parents 88e9fcf + 04b4e3e commit d85491f
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 11 deletions.
46 changes: 46 additions & 0 deletions daemon/graphdriver/graphtest/graphtest_unix.go
Expand Up @@ -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 (
Expand Down Expand Up @@ -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)
}

}
54 changes: 43 additions & 11 deletions daemon/graphdriver/zfs/zfs.go
Expand Up @@ -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
}
Expand All @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions daemon/graphdriver/zfs/zfs_test.go
Expand Up @@ -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)
}

0 comments on commit d85491f

Please sign in to comment.