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 ( | ||||
| 	"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) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue