Kaynağa Gözat

Merge pull request #21946 from chosenken/add_disk_quota_to_zfs

Add support for setting storage size on ZFS containers
Brian Goff 9 yıl önce
ebeveyn
işleme
d85491ff4b

+ 46 - 0
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)
+	}
+
+}

+ 43 - 11
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.

+ 4 - 0
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)
 }