mirror of https://github.com/docker/docs.git
Merge pull request #21946 from chosenken/add_disk_quota_to_zfs
Add support for setting storage size on ZFS containers
This commit is contained in:
commit
d85491ff4b
|
@ -5,12 +5,16 @@ package graphtest
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/docker/docker/daemon/graphdriver"
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
|
"github.com/docker/go-units"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -301,3 +305,45 @@ func DriverTestCreateSnap(t *testing.T, drivername string) {
|
||||||
|
|
||||||
verifyBase(t, driver, "Snap")
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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.
|
// 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 {
|
func (d *Driver) Create(id string, parent string, mountLabel string, storageOpt map[string]string) error {
|
||||||
if len(storageOpt) != 0 {
|
err := d.create(id, parent, storageOpt)
|
||||||
return fmt.Errorf("--storage-opt is not supported for zfs")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := d.create(id, parent)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -273,22 +269,58 @@ func (d *Driver) Create(id string, parent string, mountLabel string, storageOpt
|
||||||
}
|
}
|
||||||
|
|
||||||
// retry
|
// 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)
|
name := d.zfsPath(id)
|
||||||
|
quota, err := parseStorageOpt(storageOpt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if parent == "" {
|
if parent == "" {
|
||||||
mountoptions := map[string]string{"mountpoint": "legacy"}
|
mountoptions := map[string]string{"mountpoint": "legacy"}
|
||||||
fs, err := zfs.CreateFilesystem(name, mountoptions)
|
fs, err := zfs.CreateFilesystem(name, mountoptions)
|
||||||
|
if err == nil {
|
||||||
|
err = setQuota(name, quota)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
d.Lock()
|
d.Lock()
|
||||||
d.filesystemsCache[fs.Name] = true
|
d.filesystemsCache[fs.Name] = true
|
||||||
d.Unlock()
|
d.Unlock()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return err
|
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.
|
// Remove deletes the dataset, filesystem and the cache for the given id.
|
||||||
|
|
|
@ -26,6 +26,10 @@ func TestZfsCreateSnap(t *testing.T) {
|
||||||
graphtest.DriverTestCreateSnap(t, "zfs")
|
graphtest.DriverTestCreateSnap(t, "zfs")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestZfsSetQuota(t *testing.T) {
|
||||||
|
graphtest.DriverTestSetQuota(t, "zfs")
|
||||||
|
}
|
||||||
|
|
||||||
func TestZfsTeardown(t *testing.T) {
|
func TestZfsTeardown(t *testing.T) {
|
||||||
graphtest.PutDriver(t)
|
graphtest.PutDriver(t)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue