Bläddra i källkod

CLI flag for docker create(run) to change block device size.

Signed-off-by: Shishir Mahajan <shishir.mahajan@redhat.com>
Shishir Mahajan 9 år sedan
förälder
incheckning
b16decfccf

+ 1 - 1
contrib/docker-device-tool/device_tool.go

@@ -137,7 +137,7 @@ func main() {
 			usage()
 		}
 
-		err := devices.AddDevice(args[1], args[2])
+		err := devices.AddDevice(args[1], args[2], nil)
 		if err != nil {
 			fmt.Println("Can't create snap device: ", err)
 			os.Exit(1)

+ 3 - 1
daemon/create.go

@@ -84,6 +84,8 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig) (retC *containe
 		return nil, err
 	}
 
+	container.HostConfig.StorageOpt = params.HostConfig.StorageOpt
+
 	// Set RWLayer for container after mount labels have been set
 	if err := daemon.setRWLayer(container); err != nil {
 		return nil, err
@@ -156,7 +158,7 @@ func (daemon *Daemon) setRWLayer(container *container.Container) error {
 		}
 		layerID = img.RootFS.ChainID()
 	}
-	rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, container.MountLabel, daemon.setupInitLayer)
+	rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, container.MountLabel, daemon.setupInitLayer, container.HostConfig.StorageOpt)
 	if err != nil {
 		return err
 	}

+ 6 - 1
daemon/graphdriver/aufs/aufs.go

@@ -195,7 +195,12 @@ func (a *Driver) Exists(id string) bool {
 
 // Create three folders for each id
 // mnt, layers, and diff
-func (a *Driver) Create(id, parent, mountLabel string) error {
+func (a *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
+
+	if len(storageOpt) != 0 {
+		return fmt.Errorf("--storage-opt is not supported for aufs")
+	}
+
 	if err := a.createDirsFor(id); err != nil {
 		return err
 	}

+ 29 - 29
daemon/graphdriver/aufs/aufs_test.go

@@ -101,7 +101,7 @@ func TestCreateNewDir(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 }
@@ -110,7 +110,7 @@ func TestCreateNewDirStructure(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -131,7 +131,7 @@ func TestRemoveImage(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -156,7 +156,7 @@ func TestGetWithoutParent(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -183,7 +183,7 @@ func TestCleanupWithDir(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -196,7 +196,7 @@ func TestMountedFalseResponse(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -215,10 +215,10 @@ func TestMountedTrueReponse(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
-	if err := d.Create("2", "1", ""); err != nil {
+	if err := d.Create("2", "1", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -241,10 +241,10 @@ func TestMountWithParent(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
-	if err := d.Create("2", "1", ""); err != nil {
+	if err := d.Create("2", "1", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -272,10 +272,10 @@ func TestRemoveMountedDir(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
-	if err := d.Create("2", "1", ""); err != nil {
+	if err := d.Create("2", "1", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -311,7 +311,7 @@ func TestCreateWithInvalidParent(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "docker", ""); err == nil {
+	if err := d.Create("1", "docker", "", nil); err == nil {
 		t.Fatalf("Error should not be nil with parent does not exist")
 	}
 }
@@ -320,7 +320,7 @@ func TestGetDiff(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -354,10 +354,10 @@ func TestChanges(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
-	if err := d.Create("2", "1", ""); err != nil {
+	if err := d.Create("2", "1", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -403,7 +403,7 @@ func TestChanges(t *testing.T) {
 		t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
 	}
 
-	if err := d.Create("3", "2", ""); err != nil {
+	if err := d.Create("3", "2", "", nil); err != nil {
 		t.Fatal(err)
 	}
 	mntPoint, err = d.Get("3", "")
@@ -448,7 +448,7 @@ func TestDiffSize(t *testing.T) {
 	d := newDriver(t)
 	defer os.RemoveAll(tmp)
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -490,7 +490,7 @@ func TestChildDiffSize(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -526,7 +526,7 @@ func TestChildDiffSize(t *testing.T) {
 		t.Fatalf("Expected size to be %d got %d", size, diffSize)
 	}
 
-	if err := d.Create("2", "1", ""); err != nil {
+	if err := d.Create("2", "1", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -545,7 +545,7 @@ func TestExists(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -563,7 +563,7 @@ func TestStatus(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -592,7 +592,7 @@ func TestApplyDiff(t *testing.T) {
 	defer os.RemoveAll(tmp)
 	defer d.Cleanup()
 
-	if err := d.Create("1", "", ""); err != nil {
+	if err := d.Create("1", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -618,10 +618,10 @@ func TestApplyDiff(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if err := d.Create("2", "", ""); err != nil {
+	if err := d.Create("2", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
-	if err := d.Create("3", "2", ""); err != nil {
+	if err := d.Create("3", "2", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -671,7 +671,7 @@ func testMountMoreThan42Layers(t *testing.T, mountPath string) {
 		}
 		current = hash(current)
 
-		if err := d.Create(current, parent, ""); err != nil {
+		if err := d.Create(current, parent, "", nil); err != nil {
 			t.Logf("Current layer %d", i)
 			t.Error(err)
 		}
@@ -750,11 +750,11 @@ func BenchmarkConcurrentAccess(b *testing.B) {
 		ids = append(ids, stringid.GenerateNonCryptoID())
 	}
 
-	if err := d.Create(ids[0], "", ""); err != nil {
+	if err := d.Create(ids[0], "", "", nil); err != nil {
 		b.Fatal(err)
 	}
 
-	if err := d.Create(ids[1], ids[0], ""); err != nil {
+	if err := d.Create(ids[1], ids[0], "", nil); err != nil {
 		b.Fatal(err)
 	}
 
@@ -770,7 +770,7 @@ func BenchmarkConcurrentAccess(b *testing.B) {
 	for _, id := range ids {
 		go func(id string) {
 			defer outerGroup.Done()
-			if err := d.Create(id, parent, ""); err != nil {
+			if err := d.Create(id, parent, "", nil); err != nil {
 				b.Logf("Create %s failed", id)
 				chErr <- err
 				return

+ 6 - 1
daemon/graphdriver/btrfs/btrfs.go

@@ -242,7 +242,12 @@ func (d *Driver) subvolumesDirID(id string) string {
 }
 
 // Create the filesystem with given id.
-func (d *Driver) Create(id, parent, mountLabel string) error {
+func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
+
+	if len(storageOpt) != 0 {
+		return fmt.Errorf("--storage-opt is not supported for btrfs")
+	}
+
 	subvolumes := path.Join(d.home, "subvolumes")
 	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
 	if err != nil {

+ 1 - 1
daemon/graphdriver/btrfs/btrfs_test.go

@@ -30,7 +30,7 @@ func TestBtrfsCreateSnap(t *testing.T) {
 
 func TestBtrfsSubvolDelete(t *testing.T) {
 	d := graphtest.GetDriver(t, "btrfs")
-	if err := d.Create("test", "", ""); err != nil {
+	if err := d.Create("test", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 	defer graphtest.PutDriver(t)

+ 50 - 4
daemon/graphdriver/devmapper/deviceset.go

@@ -839,7 +839,7 @@ func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) {
 	return info, nil
 }
 
-func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInfo) error {
+func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInfo, size uint64) error {
 	if err := devices.poolHasFreeSpace(); err != nil {
 		return err
 	}
@@ -878,7 +878,7 @@ func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInf
 		break
 	}
 
-	if _, err := devices.registerDevice(deviceID, hash, baseInfo.Size, devices.OpenTransactionID); err != nil {
+	if _, err := devices.registerDevice(deviceID, hash, size, devices.OpenTransactionID); err != nil {
 		devicemapper.DeleteDevice(devices.getPoolDevName(), deviceID)
 		devices.markDeviceIDFree(deviceID)
 		logrus.Debugf("devmapper: Error registering device: %s", err)
@@ -1830,7 +1830,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
 }
 
 // AddDevice adds a device and registers in the hash.
-func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
+func (devices *DeviceSet) AddDevice(hash, baseHash string, storageOpt map[string]string) error {
 	logrus.Debugf("devmapper: AddDevice(hash=%s basehash=%s)", hash, baseHash)
 	defer logrus.Debugf("devmapper: AddDevice(hash=%s basehash=%s) END", hash, baseHash)
 
@@ -1856,10 +1856,56 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
 		return fmt.Errorf("devmapper: device %s already exists. Deleted=%v", hash, info.Deleted)
 	}
 
-	if err := devices.createRegisterSnapDevice(hash, baseInfo); err != nil {
+	devinfo := &devInfo{}
+
+	if err := devices.parseStorageOpt(storageOpt, devinfo); err != nil {
+		return err
+	}
+
+	if devinfo.Size == 0 {
+		devinfo.Size = baseInfo.Size
+	}
+
+	if devinfo.Size < baseInfo.Size {
+		return fmt.Errorf("devmapper: Container size cannot be smaller than %s", units.HumanSize(float64(baseInfo.Size)))
+	}
+
+	if err := devices.createRegisterSnapDevice(hash, baseInfo, devinfo.Size); err != nil {
 		return err
 	}
 
+	// Grow the container rootfs.
+	if devinfo.Size > 0 {
+		info, err := devices.lookupDevice(hash)
+		if err != nil {
+			return err
+		}
+
+		if err := devices.growFS(info); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (devices *DeviceSet) parseStorageOpt(storageOpt map[string]string, devinfo *devInfo) error {
+
+	// Read size to change the block device size per container.
+	for key, val := range storageOpt {
+		key := strings.ToLower(key)
+		switch key {
+		case "size":
+			size, err := units.RAMInBytes(val)
+			if err != nil {
+				return err
+			}
+			devinfo.Size = uint64(size)
+		default:
+			return fmt.Errorf("Unknown option %s", key)
+		}
+	}
+
 	return nil
 }
 

+ 2 - 2
daemon/graphdriver/devmapper/driver.go

@@ -118,8 +118,8 @@ func (d *Driver) Cleanup() error {
 }
 
 // Create adds a device with a given id and the parent.
-func (d *Driver) Create(id, parent, mountLabel string) error {
-	if err := d.DeviceSet.AddDevice(id, parent); err != nil {
+func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
+	if err := d.DeviceSet.AddDevice(id, parent, storageOpt); err != nil {
 		return err
 	}
 

+ 1 - 1
daemon/graphdriver/driver.go

@@ -48,7 +48,7 @@ type ProtoDriver interface {
 	String() string
 	// Create creates a new, empty, filesystem layer with the
 	// specified id and parent and mountLabel. Parent and mountLabel may be "".
-	Create(id, parent, mountLabel string) error
+	Create(id, parent, mountLabel string, storageOpt map[string]string) error
 	// Remove attempts to remove the filesystem layer with this id.
 	Remove(id string) error
 	// Get returns the mountpoint for the layered filesystem referred

+ 3 - 3
daemon/graphdriver/graphtest/graphtest_unix.go

@@ -177,7 +177,7 @@ func DriverTestCreateEmpty(t *testing.T, drivername string) {
 	driver := GetDriver(t, drivername)
 	defer PutDriver(t)
 
-	if err := driver.Create("empty", "", ""); err != nil {
+	if err := driver.Create("empty", "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -215,7 +215,7 @@ func createBase(t *testing.T, driver graphdriver.Driver, name string) {
 	oldmask := syscall.Umask(0)
 	defer syscall.Umask(oldmask)
 
-	if err := driver.Create(name, "", ""); err != nil {
+	if err := driver.Create(name, "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -283,7 +283,7 @@ func DriverTestCreateSnap(t *testing.T, drivername string) {
 
 	createBase(t, driver, "Base")
 
-	if err := driver.Create("Snap", "Base", ""); err != nil {
+	if err := driver.Create("Snap", "Base", "", nil); err != nil {
 		t.Fatal(err)
 	}
 

+ 6 - 1
daemon/graphdriver/overlay/overlay.go

@@ -222,7 +222,12 @@ func (d *Driver) Cleanup() error {
 
 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
 // The parent filesystem is used to configure these directories for the overlay.
-func (d *Driver) Create(id, parent, mountLabel string) (retErr error) {
+func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) (retErr error) {
+
+	if len(storageOpt) != 0 {
+		return fmt.Errorf("--storage-opt is not supported for overlay")
+	}
+
 	dir := d.dir(id)
 
 	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)

+ 1 - 1
daemon/graphdriver/proxy.go

@@ -54,7 +54,7 @@ func (d *graphDriverProxy) String() string {
 	return d.name
 }
 
-func (d *graphDriverProxy) Create(id, parent, mountLabel string) error {
+func (d *graphDriverProxy) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
 	args := &graphDriverRequest{
 		ID:         id,
 		Parent:     parent,

+ 5 - 1
daemon/graphdriver/vfs/driver.go

@@ -69,7 +69,11 @@ func (d *Driver) Cleanup() error {
 }
 
 // Create prepares the filesystem for the VFS driver and copies the directory for the given id under the parent.
-func (d *Driver) Create(id, parent, mountLabel string) error {
+func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
+	if len(storageOpt) != 0 {
+		return fmt.Errorf("--storage-opt is not supported for vfs")
+	}
+
 	dir := d.dir(id)
 	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
 	if err != nil {

+ 6 - 2
daemon/graphdriver/windows/windows.go

@@ -107,7 +107,11 @@ func (d *Driver) Exists(id string) bool {
 }
 
 // Create creates a new layer with the given id.
-func (d *Driver) Create(id, parent, mountLabel string) error {
+func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
+	if len(storageOpt) != 0 {
+		return fmt.Errorf("--storage-opt is not supported for windows")
+	}
+
 	rPId, err := d.resolveID(parent)
 	if err != nil {
 		return err
@@ -432,7 +436,7 @@ func (d *Driver) GetCustomImageInfos() ([]CustomImageInfo, error) {
 		h := sha512.Sum384([]byte(folderName))
 		id := fmt.Sprintf("%x", h[:32])
 
-		if err := d.Create(id, "", ""); err != nil {
+		if err := d.Create(id, "", "", nil); err != nil {
 			return nil, err
 		}
 		// Create the alternate ID file.

+ 5 - 1
daemon/graphdriver/zfs/zfs.go

@@ -241,7 +241,11 @@ func (d *Driver) mountPath(id string) string {
 }
 
 // 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) error {
+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)
 	if err == nil {
 		return nil

+ 1 - 1
distribution/xfer/download_test.go

@@ -106,7 +106,7 @@ func (ls *mockLayerStore) Get(chainID layer.ChainID) (layer.Layer, error) {
 func (ls *mockLayerStore) Release(l layer.Layer) ([]layer.Metadata, error) {
 	return []layer.Metadata{}, nil
 }
-func (ls *mockLayerStore) CreateRWLayer(string, layer.ChainID, string, layer.MountInit) (layer.RWLayer, error) {
+func (ls *mockLayerStore) CreateRWLayer(string, layer.ChainID, string, layer.MountInit, map[string]string) (layer.RWLayer, error) {
 	return nil, errors.New("not implemented")
 }
 

+ 2 - 0
docs/reference/api/docker_remote_api.md

@@ -116,6 +116,8 @@ This section lists each version from latest to oldest.  Each listing includes a
 
 [Docker Remote API v1.24](docker_remote_api_v1.24.md) documentation
 
+* `POST /containers/create` now takes `StorageOpt` field.
+
 ### v1.23 API changes
 
 [Docker Remote API v1.23](docker_remote_api_v1.23.md) documentation

+ 4 - 0
docs/reference/api/docker_remote_api_v1.24.md

@@ -325,6 +325,7 @@ Create a container
              "Ulimits": [{}],
              "LogConfig": { "Type": "json-file", "Config": {} },
              "SecurityOpt": [""],
+             "StorageOpt": {},
              "CgroupParent": "",
              "VolumeDriver": "",
              "ShmSize": 67108864
@@ -444,6 +445,8 @@ Json Parameters:
           `Ulimits: { "Name": "nofile", "Soft": 1024, "Hard": 2048 }`
     -   **SecurityOpt**: A list of string values to customize labels for MLS
         systems, such as SELinux.
+    -   **StorageOpt**: Storage driver options per container. Options can be passed in the form
+        `{"size":"120G"}`
     -   **LogConfig** - Log configuration for the container, specified as a JSON object in the form
           `{ "Type": "<driver_name>", "Config": {"key1": "val1"}}`.
           Available types: `json-file`, `syslog`, `journald`, `gelf`, `fluentd`, `awslogs`, `splunk`, `etwlogs`, `none`.
@@ -568,6 +571,7 @@ Return low-level information on the container `id`
 				"Type": "json-file"
 			},
 			"SecurityOpt": null,
+			"StorageOpt": null,
 			"VolumesFrom": null,
 			"Ulimits": [{}],
 			"VolumeDriver": "",

+ 8 - 0
docs/reference/commandline/create.md

@@ -81,6 +81,7 @@ Creates a new container.
       --security-opt=[]             Security options
       --stop-signal="SIGTERM"       Signal to stop a container
       --shm-size=[]                 Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater than `0`.  Unit is optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`.
+      --storage-opt=[]              Set storage driver options per container
       -t, --tty                     Allocate a pseudo-TTY
       -u, --user=""                 Username or UID
       --userns=""                   Container user namespace
@@ -145,6 +146,13 @@ then be used from the subsequent container:
     drwx--S---  2 1000 staff  460 Dec  5 00:51 .ssh
     drwxr-xr-x 32 1000 staff 1140 Dec  5 04:01 docker
 
+Set storage driver options per container. 
+
+    $ docker create -it --storage-opt size=120G fedora /bin/bash
+
+This (size) will allow to set the container rootfs size to 120G at creation time. 
+User cannot pass a size less than the Default BaseFS Size. 
+
 ### Specify isolation technology for container (--isolation)
 
 This option is useful in situations where you are running Docker containers on

+ 8 - 0
docs/reference/commandline/run.md

@@ -83,6 +83,7 @@ parent = "smn_cli"
       --security-opt=[]             Security Options
       --sig-proxy=true              Proxy received signals to the process
       --stop-signal="SIGTERM"       Signal to stop a container
+      --storage-opt=[]              Set storage driver options per container
       -t, --tty                     Allocate a pseudo-TTY
       -u, --user=""                 Username or UID (format: <name|uid>[:<group|gid>])
       --userns=""                   Container user namespace
@@ -167,6 +168,13 @@ flag exists to allow special use-cases, like running Docker within Docker.
 The `-w` lets the command being executed inside directory given, here
 `/path/to/dir/`. If the path does not exists it is created inside the container.
 
+### Set storage driver options per container
+
+    $ docker create -it --storage-opt size=120G fedora /bin/bash
+
+This (size) will allow to set the container rootfs size to 120G at creation time. 
+User cannot pass a size less than the Default BaseFS Size.
+
 ### Mount tmpfs (--tmpfs)
 
     $ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image

+ 21 - 0
integration-cli/docker_cli_create_test.go

@@ -60,6 +60,27 @@ func (s *DockerSuite) TestCreateArgs(c *check.C) {
 
 }
 
+// Make sure we can grow the container's rootfs at creation time.
+func (s *DockerSuite) TestCreateGrowRootfs(c *check.C) {
+	testRequires(c, Devicemapper)
+	out, _ := dockerCmd(c, "create", "--storage-opt", "size=120G", "busybox")
+
+	cleanedContainerID := strings.TrimSpace(out)
+
+	inspectOut := inspectField(c, cleanedContainerID, "HostConfig.StorageOpt")
+	c.Assert(inspectOut, checker.Equals, "[size=120G]")
+}
+
+// Make sure we cannot shrink the container's rootfs at creation time.
+func (s *DockerSuite) TestCreateShrinkRootfs(c *check.C) {
+	testRequires(c, Devicemapper)
+
+	// Ensure this fails
+	out, _, err := dockerCmdWithError("create", "--storage-opt", "size=80G", "busybox")
+	c.Assert(err, check.NotNil, check.Commentf(out))
+	c.Assert(out, checker.Contains, "Container size cannot be smaller than")
+}
+
 // Make sure we can set hostconfig options too
 func (s *DockerSuite) TestCreateHostConfig(c *check.C) {
 	out, _ := dockerCmd(c, "create", "-P", "busybox", "echo")

+ 1 - 1
integration-cli/docker_cli_external_graphdriver_unix_test.go

@@ -122,7 +122,7 @@ func (s *DockerExternalGraphdriverSuite) SetUpSuite(c *check.C) {
 		if err := decReq(r.Body, &req, w); err != nil {
 			return
 		}
-		if err := driver.Create(req.ID, req.Parent, ""); err != nil {
+		if err := driver.Create(req.ID, req.Parent, "", nil); err != nil {
 			respond(w, err)
 			return
 		}

+ 1 - 1
layer/layer.go

@@ -171,7 +171,7 @@ type Store interface {
 	Get(ChainID) (Layer, error)
 	Release(Layer) ([]Metadata, error)
 
-	CreateRWLayer(id string, parent ChainID, mountLabel string, initFunc MountInit) (RWLayer, error)
+	CreateRWLayer(id string, parent ChainID, mountLabel string, initFunc MountInit, storageOpt map[string]string) (RWLayer, error)
 	GetRWLayer(id string) (RWLayer, error)
 	GetMountID(id string) (string, error)
 	ReleaseRWLayer(RWLayer) ([]Metadata, error)

+ 6 - 6
layer/layer_store.go

@@ -263,7 +263,7 @@ func (ls *layerStore) Register(ts io.Reader, parent ChainID) (Layer, error) {
 		references:     map[Layer]struct{}{},
 	}
 
-	if err = ls.driver.Create(layer.cacheID, pid, ""); err != nil {
+	if err = ls.driver.Create(layer.cacheID, pid, "", nil); err != nil {
 		return nil, err
 	}
 
@@ -414,7 +414,7 @@ func (ls *layerStore) Release(l Layer) ([]Metadata, error) {
 	return ls.releaseLayer(layer)
 }
 
-func (ls *layerStore) CreateRWLayer(name string, parent ChainID, mountLabel string, initFunc MountInit) (RWLayer, error) {
+func (ls *layerStore) CreateRWLayer(name string, parent ChainID, mountLabel string, initFunc MountInit, storageOpt map[string]string) (RWLayer, error) {
 	ls.mountL.Lock()
 	defer ls.mountL.Unlock()
 	m, ok := ls.mounts[name]
@@ -451,14 +451,14 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, mountLabel stri
 	}
 
 	if initFunc != nil {
-		pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc)
+		pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc, storageOpt)
 		if err != nil {
 			return nil, err
 		}
 		m.initID = pid
 	}
 
-	if err = ls.driver.Create(m.mountID, pid, ""); err != nil {
+	if err = ls.driver.Create(m.mountID, pid, "", storageOpt); err != nil {
 		return nil, err
 	}
 
@@ -561,14 +561,14 @@ func (ls *layerStore) saveMount(mount *mountedLayer) error {
 	return nil
 }
 
-func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc MountInit) (string, error) {
+func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc MountInit, storageOpt map[string]string) (string, error) {
 	// Use "<graph-id>-init" to maintain compatibility with graph drivers
 	// which are expecting this layer with this special name. If all
 	// graph drivers can be updated to not rely on knowing about this layer
 	// then the initID should be randomly generated.
 	initID := fmt.Sprintf("%s-init", graphID)
 
-	if err := ls.driver.Create(initID, parent, mountLabel); err != nil {
+	if err := ls.driver.Create(initID, parent, mountLabel, storageOpt); err != nil {
 		return "", err
 	}
 	p, err := ls.driver.Get(initID, "")

+ 4 - 4
layer/layer_test.go

@@ -84,7 +84,7 @@ type layerInit func(root string) error
 
 func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) {
 	containerID := stringid.GenerateRandomID()
-	mount, err := ls.CreateRWLayer(containerID, parent, "", nil)
+	mount, err := ls.CreateRWLayer(containerID, parent, "", nil, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -279,7 +279,7 @@ func TestMountAndRegister(t *testing.T) {
 	size, _ := layer.Size()
 	t.Logf("Layer size: %d", size)
 
-	mount2, err := ls.CreateRWLayer("new-test-mount", layer.ChainID(), "", nil)
+	mount2, err := ls.CreateRWLayer("new-test-mount", layer.ChainID(), "", nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -387,7 +387,7 @@ func TestStoreRestore(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	m, err := ls.CreateRWLayer("some-mount_name", layer3.ChainID(), "", nil)
+	m, err := ls.CreateRWLayer("some-mount_name", layer3.ChainID(), "", nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -421,7 +421,7 @@ func TestStoreRestore(t *testing.T) {
 	assertLayerEqual(t, layer3b, layer3)
 
 	// Create again with same name, should return error
-	if _, err := ls2.CreateRWLayer("some-mount_name", layer3b.ChainID(), "", nil); err == nil {
+	if _, err := ls2.CreateRWLayer("some-mount_name", layer3b.ChainID(), "", nil, nil); err == nil {
 		t.Fatal("Expected error creating mount with same name")
 	} else if err != ErrMountNameConflict {
 		t.Fatal(err)

+ 6 - 6
layer/migration_test.go

@@ -78,7 +78,7 @@ func TestLayerMigration(t *testing.T) {
 	}
 
 	graphID1 := stringid.GenerateRandomID()
-	if err := graph.Create(graphID1, "", ""); err != nil {
+	if err := graph.Create(graphID1, "", "", nil); err != nil {
 		t.Fatal(err)
 	}
 	if _, err := graph.ApplyDiff(graphID1, "", archive.Reader(bytes.NewReader(tar1))); err != nil {
@@ -123,7 +123,7 @@ func TestLayerMigration(t *testing.T) {
 	}
 
 	graphID2 := stringid.GenerateRandomID()
-	if err := graph.Create(graphID2, graphID1, ""); err != nil {
+	if err := graph.Create(graphID2, graphID1, "", nil); err != nil {
 		t.Fatal(err)
 	}
 	if _, err := graph.ApplyDiff(graphID2, graphID1, archive.Reader(bytes.NewReader(tar2))); err != nil {
@@ -165,7 +165,7 @@ func tarFromFilesInGraph(graph graphdriver.Driver, graphID, parentID string, fil
 		return nil, err
 	}
 
-	if err := graph.Create(graphID, parentID, ""); err != nil {
+	if err := graph.Create(graphID, parentID, "", nil); err != nil {
 		return nil, err
 	}
 	if _, err := graph.ApplyDiff(graphID, parentID, archive.Reader(bytes.NewReader(t))); err != nil {
@@ -320,14 +320,14 @@ func TestMountMigration(t *testing.T) {
 	containerID := stringid.GenerateRandomID()
 	containerInit := fmt.Sprintf("%s-init", containerID)
 
-	if err := graph.Create(containerInit, graphID1, ""); err != nil {
+	if err := graph.Create(containerInit, graphID1, "", nil); err != nil {
 		t.Fatal(err)
 	}
 	if _, err := graph.ApplyDiff(containerInit, graphID1, archive.Reader(bytes.NewReader(initTar))); err != nil {
 		t.Fatal(err)
 	}
 
-	if err := graph.Create(containerID, containerInit, ""); err != nil {
+	if err := graph.Create(containerID, containerInit, "", nil); err != nil {
 		t.Fatal(err)
 	}
 	if _, err := graph.ApplyDiff(containerID, containerInit, archive.Reader(bytes.NewReader(mountTar))); err != nil {
@@ -382,7 +382,7 @@ func TestMountMigration(t *testing.T) {
 
 	assertActivityCount(t, rwLayer1, 1)
 
-	if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), "", nil); err == nil {
+	if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), "", nil, nil); err == nil {
 		t.Fatal("Expected error creating mount with same name")
 	} else if err != ErrMountNameConflict {
 		t.Fatal(err)

+ 3 - 3
layer/mount_test.go

@@ -32,7 +32,7 @@ func TestMountInit(t *testing.T) {
 		return initfile.ApplyFile(root)
 	}
 
-	m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), "", mountInit)
+	m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), "", mountInit, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -89,7 +89,7 @@ func TestMountSize(t *testing.T) {
 		return newTestFile("file-init", contentInit, 0777).ApplyFile(root)
 	}
 
-	m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), "", mountInit)
+	m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), "", mountInit, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -138,7 +138,7 @@ func TestMountChanges(t *testing.T) {
 		return initfile.ApplyFile(root)
 	}
 
-	m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), "", mountInit)
+	m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), "", mountInit, nil)
 	if err != nil {
 		t.Fatal(err)
 	}

+ 8 - 0
man/docker-create.1.md

@@ -64,6 +64,7 @@ docker-create - Create a new container
 [**--read-only**]
 [**--restart**[=*RESTART*]]
 [**--security-opt**[=*[]*]]
+[**--storage-opt**[=*[]*]]
 [**--stop-signal**[=*SIGNAL*]]
 [**--shm-size**[=*[]*]]
 [**-t**|**--tty**]
@@ -325,6 +326,13 @@ unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
     "seccomp:unconfined" : Turn off seccomp confinement for the container
     "seccomp:profile.json :  White listed syscalls seccomp Json file to be used as a seccomp filter
 
+**--storage-opt**=[]
+   Storage driver options per container
+
+   $ docker create -it --storage-opt size=120G fedora /bin/bash
+
+   This (size) will allow to set the container rootfs size to 120G at creation time. User cannot pass a size less than the Default BaseFS Size.
+  
 **--stop-signal**=*SIGTERM*
   Signal to stop a container. Default is SIGTERM.
 

+ 8 - 0
man/docker-run.1.md

@@ -67,6 +67,7 @@ docker-run - Run a command in a new container
 [**--restart**[=*RESTART*]]
 [**--rm**]
 [**--security-opt**[=*[]*]]
+[**--storage-opt**[=*[]*]]
 [**--stop-signal**[=*SIGNAL*]]
 [**--shm-size**[=*[]*]]
 [**--sig-proxy**[=*true*]]
@@ -476,6 +477,13 @@ its root filesystem mounted as read only prohibiting any writes.
     "apparmor=unconfined" : Turn off apparmor confinement for the container
     "apparmor=your-profile" : Set the apparmor confinement profile for the container
 
+**--storage-opt**=[]
+   Storage driver options per container
+
+   $ docker run -it --storage-opt size=120G fedora /bin/bash
+
+   This (size) will allow to set the container rootfs size to 120G at creation time. User cannot pass a size less than the Default BaseFS Size.
+  
 **--stop-signal**=*SIGTERM*
   Signal to stop a container. Default is SIGTERM.
 

+ 22 - 0
runconfig/opts/parse.go

@@ -55,6 +55,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
 		flCapDrop           = opts.NewListOpts(nil)
 		flGroupAdd          = opts.NewListOpts(nil)
 		flSecurityOpt       = opts.NewListOpts(nil)
+		flStorageOpt        = opts.NewListOpts(nil)
 		flLabelsFile        = opts.NewListOpts(nil)
 		flLoggingOpts       = opts.NewListOpts(nil)
 		flPrivileged        = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to this container")
@@ -124,6 +125,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
 	cmd.Var(&flCapDrop, []string{"-cap-drop"}, "Drop Linux capabilities")
 	cmd.Var(&flGroupAdd, []string{"-group-add"}, "Add additional groups to join")
 	cmd.Var(&flSecurityOpt, []string{"-security-opt"}, "Security Options")
+	cmd.Var(&flStorageOpt, []string{"-storage-opt"}, "Set storage driver options per container")
 	cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
 	cmd.Var(&flLoggingOpts, []string{"-log-opt"}, "Log driver options")
 
@@ -337,6 +339,11 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
 		return nil, nil, nil, cmd, err
 	}
 
+	storageOpts, err := parseStorageOpts(flStorageOpt.GetAll())
+	if err != nil {
+		return nil, nil, nil, cmd, err
+	}
+
 	resources := container.Resources{
 		CgroupParent:         *flCgroupParent,
 		Memory:               flMemory,
@@ -415,6 +422,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
 		GroupAdd:       flGroupAdd.GetAll(),
 		RestartPolicy:  restartPolicy,
 		SecurityOpt:    securityOpts,
+		StorageOpt:     storageOpts,
 		ReadonlyRootfs: *flReadonlyRootfs,
 		LogConfig:      container.LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
 		VolumeDriver:   *flVolumeDriver,
@@ -531,6 +539,20 @@ func parseSecurityOpts(securityOpts []string) ([]string, error) {
 	return securityOpts, nil
 }
 
+// parse storage options per container into a map
+func parseStorageOpts(storageOpts []string) (map[string]string, error) {
+	m := make(map[string]string)
+	for _, option := range storageOpts {
+		if strings.Contains(option, "=") {
+			opt := strings.SplitN(option, "=", 2)
+			m[opt[0]] = opt[1]
+		} else {
+			return nil, fmt.Errorf("Invalid storage option.")
+		}
+	}
+	return m, nil
+}
+
 // ParseRestartPolicy returns the parsed policy or an error indicating what is incorrect
 func ParseRestartPolicy(policy string) (container.RestartPolicy, error) {
 	p := container.RestartPolicy{}