mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #6224 from tiborvass/storage-options
Add --storage-opt daemon option and some devicemapper option (with fixes)
This commit is contained in:
commit
9329c0d2e0
20 changed files with 532 additions and 97 deletions
|
@ -780,7 +780,7 @@ func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*D
|
||||||
graphdriver.DefaultDriver = config.GraphDriver
|
graphdriver.DefaultDriver = config.GraphDriver
|
||||||
|
|
||||||
// Load storage driver
|
// Load storage driver
|
||||||
driver, err := graphdriver.New(config.Root)
|
driver, err := graphdriver.New(config.Root, config.GraphOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -809,7 +809,7 @@ func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*D
|
||||||
|
|
||||||
// We don't want to use a complex driver like aufs or devmapper
|
// We don't want to use a complex driver like aufs or devmapper
|
||||||
// for volumes, just a plain filesystem
|
// for volumes, just a plain filesystem
|
||||||
volumesDriver, err := graphdriver.GetDriver("vfs", config.Root)
|
volumesDriver, err := graphdriver.GetDriver("vfs", config.Root, config.GraphOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ type Driver struct {
|
||||||
|
|
||||||
// New returns a new AUFS driver.
|
// New returns a new AUFS driver.
|
||||||
// An error is returned if AUFS is not supported.
|
// An error is returned if AUFS is not supported.
|
||||||
func Init(root string) (graphdriver.Driver, error) {
|
func Init(root string, options []string) (graphdriver.Driver, error) {
|
||||||
// Try to load the aufs kernel module
|
// Try to load the aufs kernel module
|
||||||
if err := supportsAufs(); err != nil {
|
if err := supportsAufs(); err != nil {
|
||||||
return nil, graphdriver.ErrNotSupported
|
return nil, graphdriver.ErrNotSupported
|
||||||
|
|
|
@ -17,7 +17,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func testInit(dir string, t *testing.T) graphdriver.Driver {
|
func testInit(dir string, t *testing.T) graphdriver.Driver {
|
||||||
d, err := Init(dir)
|
d, err := Init(dir, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == graphdriver.ErrNotSupported {
|
if err == graphdriver.ErrNotSupported {
|
||||||
t.Skip(err)
|
t.Skip(err)
|
||||||
|
|
|
@ -22,7 +22,7 @@ func init() {
|
||||||
graphdriver.Register("btrfs", Init)
|
graphdriver.Register("btrfs", Init)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(home string) (graphdriver.Driver, error) {
|
func Init(home string, options []string) (graphdriver.Driver, error) {
|
||||||
rootdir := path.Dir(home)
|
rootdir := path.Dir(home)
|
||||||
|
|
||||||
var buf syscall.Statfs_t
|
var buf syscall.Statfs_t
|
||||||
|
|
143
daemon/graphdriver/devmapper/README.md
Normal file
143
daemon/graphdriver/devmapper/README.md
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
## devicemapper - a storage backend based on Device Mapper
|
||||||
|
|
||||||
|
### Theory of operation
|
||||||
|
|
||||||
|
The device mapper graphdriver uses the device mapper thin provisioning
|
||||||
|
module (dm-thinp) to implement CoW snapshots. For each devicemapper
|
||||||
|
graph location (typically `/var/lib/docker/devicemapper`, $graph below)
|
||||||
|
a thin pool is created based on two block devices, one for data and
|
||||||
|
one for metadata. By default these block devices are created
|
||||||
|
automatically by using loopback mounts of automatically creates sparse
|
||||||
|
files.
|
||||||
|
|
||||||
|
The default loopback files used are `$graph/devicemapper/data` and
|
||||||
|
`$graph/devicemapper/metadata`. Additional metadata required to map
|
||||||
|
from docker entities to the corresponding devicemapper volumes is
|
||||||
|
stored in the `$graph/devicemapper/json` file (encoded as Json).
|
||||||
|
|
||||||
|
In order to support multiple devicemapper graphs on a system the thin
|
||||||
|
pool will be named something like: `docker-0:33-19478248-pool`, where
|
||||||
|
the `0:30` part is the minor/major device nr and `19478248` is the
|
||||||
|
inode number of the $graph directory.
|
||||||
|
|
||||||
|
On the thin pool docker automatically creates a base thin device,
|
||||||
|
called something like `docker-0:33-19478248-base` of a fixed
|
||||||
|
size. This is automatically formated on creation and contains just an
|
||||||
|
empty filesystem. This device is the base of all docker images and
|
||||||
|
containers. All base images are snapshots of this device and those
|
||||||
|
images are then in turn used as snapshots for other images and
|
||||||
|
eventually containers.
|
||||||
|
|
||||||
|
### options
|
||||||
|
|
||||||
|
The devicemapper backend supports some options that you can specify
|
||||||
|
when starting the docker daemon using the --storage-opt flags.
|
||||||
|
This uses the `dm` prefix and would be used somthing like `docker -d --storage-opt dm.foo=bar`.
|
||||||
|
|
||||||
|
Here is the list of supported options:
|
||||||
|
|
||||||
|
* `dm.basesize`
|
||||||
|
|
||||||
|
Specifies the size to use when creating the base device, which
|
||||||
|
limits the size of images and containers. The default value is
|
||||||
|
10G. Note, thin devices are inherently "sparse", so a 10G device
|
||||||
|
which is mostly empty doesn't use 10 GB of space on the
|
||||||
|
pool. However, the filesystem will use more space for the empty
|
||||||
|
case the larger the device is.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
|
||||||
|
``docker -d --storage-opt dm.basesize=20G``
|
||||||
|
|
||||||
|
* `dm.loopdatasize`
|
||||||
|
|
||||||
|
Specifies the size to use when creating the loopback file for the
|
||||||
|
"data" device which is used for the thin pool. The default size is
|
||||||
|
100G. Note that the file is sparse, so it will not initially take
|
||||||
|
up this much space.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
|
||||||
|
``docker -d --storage-opt dm.loopdatasize=200G``
|
||||||
|
|
||||||
|
* `dm.loopmetadatasize`
|
||||||
|
|
||||||
|
Specifies the size to use when creating the loopback file for the
|
||||||
|
"metadadata" device which is used for the thin pool. The default size is
|
||||||
|
2G. Note that the file is sparse, so it will not initially take
|
||||||
|
up this much space.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
|
||||||
|
``docker -d --storage-opt dm.loopmetadatasize=4G``
|
||||||
|
|
||||||
|
* `dm.fs`
|
||||||
|
|
||||||
|
Specifies the filesystem type to use for the base device. The supported
|
||||||
|
options are "ext4" and "xfs". The default is "ext4"
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
|
||||||
|
``docker -d --storage-opt dm.fs=xfs``
|
||||||
|
|
||||||
|
* `dm.mkfsarg`
|
||||||
|
|
||||||
|
Specifies extra mkfs arguments to be used when creating the base device.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
|
||||||
|
``docker -d --storage-opt "dm.mkfsarg=-O ^has_journal"``
|
||||||
|
|
||||||
|
* `dm.mountopt`
|
||||||
|
|
||||||
|
Specifies extra mount options used when mounting the thin devices.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
|
||||||
|
``docker -d --storage-opt dm.mountopt=nodiscard``
|
||||||
|
|
||||||
|
* `dm.datadev`
|
||||||
|
|
||||||
|
Specifies a custom blockdevice to use for data for the thin pool.
|
||||||
|
|
||||||
|
If using a block device for device mapper storage, ideally both
|
||||||
|
datadev and metadatadev should be specified to completely avoid
|
||||||
|
using the loopback device.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
|
||||||
|
``docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1``
|
||||||
|
|
||||||
|
* `dm.metadatadev`
|
||||||
|
|
||||||
|
Specifies a custom blockdevice to use for metadata for the thin
|
||||||
|
pool.
|
||||||
|
|
||||||
|
For best performance the metadata should be on a different spindle
|
||||||
|
than the data, or even better on an SSD.
|
||||||
|
|
||||||
|
If setting up a new metadata pool it is required to be valid. This
|
||||||
|
can be achieved by zeroing the first 4k to indicate empty
|
||||||
|
metadata, like this:
|
||||||
|
|
||||||
|
``dd if=/dev/zero of=$metadata_dev bs=4096 count=1```
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
|
||||||
|
``docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1``
|
||||||
|
|
||||||
|
* `dm.blkdiscard`
|
||||||
|
|
||||||
|
Enables or disables the use of blkdiscard when removing
|
||||||
|
devicemapper devices. This is enabled by default (only) if using
|
||||||
|
loopback devices and is required to res-parsify the loopback file
|
||||||
|
on image/container removal.
|
||||||
|
|
||||||
|
Disabling this on loopback can lead to *much* faster container
|
||||||
|
removal times, but will make the space used in /var/lib/docker
|
||||||
|
directory not be returned to the system for other use when
|
||||||
|
containers are removed.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
|
||||||
|
``docker -d --storage-opt dm.blkdiscard=false``
|
|
@ -13,11 +13,14 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/daemon/graphdriver"
|
||||||
"github.com/dotcloud/docker/pkg/label"
|
"github.com/dotcloud/docker/pkg/label"
|
||||||
|
"github.com/dotcloud/docker/pkg/units"
|
||||||
"github.com/dotcloud/docker/utils"
|
"github.com/dotcloud/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,6 +67,17 @@ type DeviceSet struct {
|
||||||
TransactionId uint64
|
TransactionId uint64
|
||||||
NewTransactionId uint64
|
NewTransactionId uint64
|
||||||
nextDeviceId int
|
nextDeviceId int
|
||||||
|
|
||||||
|
// Options
|
||||||
|
dataLoopbackSize int64
|
||||||
|
metaDataLoopbackSize int64
|
||||||
|
baseFsSize uint64
|
||||||
|
filesystem string
|
||||||
|
mountOptions string
|
||||||
|
mkfsArgs []string
|
||||||
|
dataDevice string
|
||||||
|
metadataDevice string
|
||||||
|
doBlkDiscard bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type DiskUsage struct {
|
type DiskUsage struct {
|
||||||
|
@ -273,26 +287,39 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *DevInfo) error {
|
||||||
func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
|
func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
|
||||||
devname := info.DevName()
|
devname := info.DevName()
|
||||||
|
|
||||||
err := exec.Command("mkfs.ext4", "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0", devname).Run()
|
args := []string{}
|
||||||
if err != nil {
|
for _, arg := range devices.mkfsArgs {
|
||||||
err = exec.Command("mkfs.ext4", "-E", "nodiscard,lazy_itable_init=0", devname).Run()
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, devname)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch devices.filesystem {
|
||||||
|
case "xfs":
|
||||||
|
err = exec.Command("mkfs.xfs", args...).Run()
|
||||||
|
case "ext4":
|
||||||
|
err = exec.Command("mkfs.ext4", append([]string{"-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0"}, args...)...).Run()
|
||||||
|
if err != nil {
|
||||||
|
err = exec.Command("mkfs.ext4", append([]string{"-E", "nodiscard,lazy_itable_init=0"}, args...)...).Run()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Unsupported filesystem type %s", devices.filesystem)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (devices *DeviceSet) initMetaData() error {
|
func (devices *DeviceSet) initMetaData() error {
|
||||||
_, _, _, params, err := getStatus(devices.getPoolName())
|
_, _, _, params, err := getStatus(devices.getPoolName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil {
|
if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
devices.NewTransactionId = devices.TransactionId
|
devices.NewTransactionId = devices.TransactionId
|
||||||
|
@ -301,7 +328,6 @@ func (devices *DeviceSet) initMetaData() error {
|
||||||
|
|
||||||
jsonData, err := ioutil.ReadFile(devices.oldMetadataFile())
|
jsonData, err := ioutil.ReadFile(devices.oldMetadataFile())
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +335,6 @@ func (devices *DeviceSet) initMetaData() error {
|
||||||
m := MetaData{Devices: make(map[string]*DevInfo)}
|
m := MetaData{Devices: make(map[string]*DevInfo)}
|
||||||
|
|
||||||
if err := json.Unmarshal(jsonData, &m); err != nil {
|
if err := json.Unmarshal(jsonData, &m); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +384,6 @@ func (devices *DeviceSet) setupBaseImage() error {
|
||||||
if oldInfo != nil && !oldInfo.Initialized {
|
if oldInfo != nil && !oldInfo.Initialized {
|
||||||
utils.Debugf("Removing uninitialized base image")
|
utils.Debugf("Removing uninitialized base image")
|
||||||
if err := devices.deleteDevice(oldInfo); err != nil {
|
if err := devices.deleteDevice(oldInfo); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -370,37 +394,32 @@ func (devices *DeviceSet) setupBaseImage() error {
|
||||||
|
|
||||||
// Create initial device
|
// Create initial device
|
||||||
if err := createDevice(devices.getPoolDevName(), &id); err != nil {
|
if err := createDevice(devices.getPoolDevName(), &id); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ids are 24bit, so wrap around
|
// Ids are 24bit, so wrap around
|
||||||
devices.nextDeviceId = (id + 1) & 0xffffff
|
devices.nextDeviceId = (id + 1) & 0xffffff
|
||||||
|
|
||||||
utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize)
|
utils.Debugf("Registering base device (id %v) with FS size %v", id, devices.baseFsSize)
|
||||||
info, err := devices.registerDevice(id, "", DefaultBaseFsSize)
|
info, err := devices.registerDevice(id, "", devices.baseFsSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = deleteDevice(devices.getPoolDevName(), id)
|
_ = deleteDevice(devices.getPoolDevName(), id)
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.Debugf("Creating filesystem on base device-manager snapshot")
|
utils.Debugf("Creating filesystem on base device-manager snapshot")
|
||||||
|
|
||||||
if err = devices.activateDeviceIfNeeded(info); err != nil {
|
if err = devices.activateDeviceIfNeeded(info); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := devices.createFilesystem(info); err != nil {
|
if err := devices.createFilesystem(info); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
info.Initialized = true
|
info.Initialized = true
|
||||||
if err = devices.saveMetadata(info); err != nil {
|
if err = devices.saveMetadata(info); err != nil {
|
||||||
info.Initialized = false
|
info.Initialized = false
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,6 +525,12 @@ func (devices *DeviceSet) ResizePool(size int64) error {
|
||||||
func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
||||||
logInit(devices)
|
logInit(devices)
|
||||||
|
|
||||||
|
_, err := getDriverVersion()
|
||||||
|
if err != nil {
|
||||||
|
// Can't even get driver version, assume not supported
|
||||||
|
return graphdriver.ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil && !os.IsExist(err) {
|
if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil && !os.IsExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -548,45 +573,74 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
||||||
if info.Exists == 0 {
|
if info.Exists == 0 {
|
||||||
utils.Debugf("Pool doesn't exist. Creating it.")
|
utils.Debugf("Pool doesn't exist. Creating it.")
|
||||||
|
|
||||||
hasData := devices.hasImage("data")
|
var (
|
||||||
hasMetadata := devices.hasImage("metadata")
|
dataFile *os.File
|
||||||
|
metadataFile *os.File
|
||||||
|
)
|
||||||
|
|
||||||
if !doInit && !hasData {
|
if devices.dataDevice == "" {
|
||||||
return errors.New("Loopback data file not found")
|
// Make sure the sparse images exist in <root>/devicemapper/data
|
||||||
}
|
|
||||||
|
|
||||||
if !doInit && !hasMetadata {
|
hasData := devices.hasImage("data")
|
||||||
return errors.New("Loopback metadata file not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
createdLoopback = !hasData || !hasMetadata
|
if !doInit && !hasData {
|
||||||
data, err := devices.ensureImage("data", DefaultDataLoopbackSize)
|
return errors.New("Loopback data file not found")
|
||||||
if err != nil {
|
}
|
||||||
utils.Debugf("Error device ensureImage (data): %s\n", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize)
|
|
||||||
if err != nil {
|
|
||||||
utils.Debugf("Error device ensureImage (metadata): %s\n", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dataFile, err := attachLoopDevice(data)
|
if !hasData {
|
||||||
if err != nil {
|
createdLoopback = true
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
}
|
||||||
return err
|
|
||||||
|
data, err := devices.ensureImage("data", devices.dataLoopbackSize)
|
||||||
|
if err != nil {
|
||||||
|
utils.Debugf("Error device ensureImage (data): %s\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataFile, err = attachLoopDevice(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataFile, err = os.OpenFile(devices.dataDevice, os.O_RDWR, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defer dataFile.Close()
|
defer dataFile.Close()
|
||||||
|
|
||||||
metadataFile, err := attachLoopDevice(metadata)
|
if devices.metadataDevice == "" {
|
||||||
if err != nil {
|
// Make sure the sparse images exist in <root>/devicemapper/metadata
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
hasMetadata := devices.hasImage("metadata")
|
||||||
|
|
||||||
|
if !doInit && !hasMetadata {
|
||||||
|
return errors.New("Loopback metadata file not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasMetadata {
|
||||||
|
createdLoopback = true
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata, err := devices.ensureImage("metadata", devices.metaDataLoopbackSize)
|
||||||
|
if err != nil {
|
||||||
|
utils.Debugf("Error device ensureImage (metadata): %s\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataFile, err = attachLoopDevice(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
metadataFile, err = os.OpenFile(devices.metadataDevice, os.O_RDWR, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defer metadataFile.Close()
|
defer metadataFile.Close()
|
||||||
|
|
||||||
if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
|
if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -595,7 +649,6 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
||||||
// load the transaction id and migrate old metadata
|
// load the transaction id and migrate old metadata
|
||||||
if !createdLoopback {
|
if !createdLoopback {
|
||||||
if err = devices.initMetaData(); err != nil {
|
if err = devices.initMetaData(); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -646,12 +699,14 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
|
func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
|
||||||
// This is a workaround for the kernel not discarding block so
|
if devices.doBlkDiscard {
|
||||||
// on the thin pool when we remove a thinp device, so we do it
|
// This is a workaround for the kernel not discarding block so
|
||||||
// manually
|
// on the thin pool when we remove a thinp device, so we do it
|
||||||
if err := devices.activateDeviceIfNeeded(info); err == nil {
|
// manually
|
||||||
if err := BlockDeviceDiscard(info.DevName()); err != nil {
|
if err := devices.activateDeviceIfNeeded(info); err == nil {
|
||||||
utils.Debugf("Error discarding block on device: %s (ignoring)\n", err)
|
if err := BlockDeviceDiscard(info.DevName()); err != nil {
|
||||||
|
utils.Debugf("Error discarding block on device: %s (ignoring)\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,7 +760,6 @@ func (devices *DeviceSet) deactivatePool() error {
|
||||||
devname := devices.getPoolDevName()
|
devname := devices.getPoolDevName()
|
||||||
devinfo, err := getInfo(devname)
|
devinfo, err := getInfo(devname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if devinfo.Exists != 0 {
|
if devinfo.Exists != 0 {
|
||||||
|
@ -727,12 +781,10 @@ func (devices *DeviceSet) deactivateDevice(info *DevInfo) error {
|
||||||
|
|
||||||
devinfo, err := getInfo(info.Name())
|
devinfo, err := getInfo(info.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if devinfo.Exists != 0 {
|
if devinfo.Exists != 0 {
|
||||||
if err := devices.removeDeviceAndWait(info.Name()); err != nil {
|
if err := devices.removeDeviceAndWait(info.Name()); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -907,11 +959,24 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
|
||||||
|
|
||||||
var flags uintptr = syscall.MS_MGC_VAL
|
var flags uintptr = syscall.MS_MGC_VAL
|
||||||
|
|
||||||
mountOptions := label.FormatMountLabel("discard", mountLabel)
|
fstype, err := ProbeFsType(info.DevName())
|
||||||
err = syscall.Mount(info.DevName(), path, "ext4", flags, mountOptions)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
options := ""
|
||||||
|
|
||||||
|
if fstype == "xfs" {
|
||||||
|
// XFS needs nouuid or it can't mount filesystems with the same fs
|
||||||
|
options = joinMountOptions(options, "nouuid")
|
||||||
|
}
|
||||||
|
|
||||||
|
options = joinMountOptions(options, devices.mountOptions)
|
||||||
|
options = joinMountOptions(options, label.FormatMountLabel("", mountLabel))
|
||||||
|
|
||||||
|
err = syscall.Mount(info.DevName(), path, fstype, flags, joinMountOptions("discard", options))
|
||||||
if err != nil && err == syscall.EINVAL {
|
if err != nil && err == syscall.EINVAL {
|
||||||
mountOptions = label.FormatMountLabel("", mountLabel)
|
err = syscall.Mount(info.DevName(), path, fstype, flags, options)
|
||||||
err = syscall.Mount(info.DevName(), path, "ext4", flags, mountOptions)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
|
return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
|
||||||
|
@ -949,7 +1014,6 @@ func (devices *DeviceSet) UnmountDevice(hash string) error {
|
||||||
|
|
||||||
utils.Debugf("[devmapper] Unmount(%s)", info.mountPath)
|
utils.Debugf("[devmapper] Unmount(%s)", info.mountPath)
|
||||||
if err := syscall.Unmount(info.mountPath, 0); err != nil {
|
if err := syscall.Unmount(info.mountPath, 0); err != nil {
|
||||||
utils.Debugf("\n--->Err: %s\n", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
utils.Debugf("[devmapper] Unmount done")
|
utils.Debugf("[devmapper] Unmount done")
|
||||||
|
@ -1084,12 +1148,72 @@ func (devices *DeviceSet) Status() *Status {
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) {
|
func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error) {
|
||||||
SetDevDir("/dev")
|
SetDevDir("/dev")
|
||||||
|
|
||||||
devices := &DeviceSet{
|
devices := &DeviceSet{
|
||||||
root: root,
|
root: root,
|
||||||
MetaData: MetaData{Devices: make(map[string]*DevInfo)},
|
MetaData: MetaData{Devices: make(map[string]*DevInfo)},
|
||||||
|
dataLoopbackSize: DefaultDataLoopbackSize,
|
||||||
|
metaDataLoopbackSize: DefaultMetaDataLoopbackSize,
|
||||||
|
baseFsSize: DefaultBaseFsSize,
|
||||||
|
filesystem: "ext4",
|
||||||
|
doBlkDiscard: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
foundBlkDiscard := false
|
||||||
|
for _, option := range options {
|
||||||
|
key, val, err := utils.ParseKeyValueOpt(option)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key = strings.ToLower(key)
|
||||||
|
switch key {
|
||||||
|
case "dm.basesize":
|
||||||
|
size, err := units.FromHumanSize(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
devices.baseFsSize = uint64(size)
|
||||||
|
case "dm.loopdatasize":
|
||||||
|
size, err := units.FromHumanSize(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
devices.dataLoopbackSize = size
|
||||||
|
case "dm.loopmetadatasize":
|
||||||
|
size, err := units.FromHumanSize(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
devices.metaDataLoopbackSize = size
|
||||||
|
case "dm.fs":
|
||||||
|
if val != "ext4" && val != "xfs" {
|
||||||
|
return nil, fmt.Errorf("Unsupported filesystem %s\n", val)
|
||||||
|
}
|
||||||
|
devices.filesystem = val
|
||||||
|
case "dm.mkfsarg":
|
||||||
|
devices.mkfsArgs = append(devices.mkfsArgs, val)
|
||||||
|
case "dm.mountopt":
|
||||||
|
devices.mountOptions = joinMountOptions(devices.mountOptions, val)
|
||||||
|
case "dm.metadatadev":
|
||||||
|
devices.metadataDevice = val
|
||||||
|
case "dm.datadev":
|
||||||
|
devices.dataDevice = val
|
||||||
|
case "dm.blkdiscard":
|
||||||
|
foundBlkDiscard = true
|
||||||
|
devices.doBlkDiscard, err = strconv.ParseBool(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Unknown option %s\n", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, don't do blk discard hack on raw devices, its rarely useful and is expensive
|
||||||
|
if !foundBlkDiscard && devices.dataDevice != "" {
|
||||||
|
devices.doBlkDiscard = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := devices.initDevmapper(doInit); err != nil {
|
if err := devices.initDevmapper(doInit); err != nil {
|
||||||
|
|
|
@ -52,6 +52,7 @@ var (
|
||||||
ErrTaskAddTarget = errors.New("dm_task_add_target failed")
|
ErrTaskAddTarget = errors.New("dm_task_add_target failed")
|
||||||
ErrTaskSetSector = errors.New("dm_task_set_sector failed")
|
ErrTaskSetSector = errors.New("dm_task_set_sector failed")
|
||||||
ErrTaskGetInfo = errors.New("dm_task_get_info failed")
|
ErrTaskGetInfo = errors.New("dm_task_get_info failed")
|
||||||
|
ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed")
|
||||||
ErrTaskSetCookie = errors.New("dm_task_set_cookie failed")
|
ErrTaskSetCookie = errors.New("dm_task_set_cookie failed")
|
||||||
ErrNilCookie = errors.New("cookie ptr can't be nil")
|
ErrNilCookie = errors.New("cookie ptr can't be nil")
|
||||||
ErrAttachLoopbackDevice = errors.New("loopback mounting failed")
|
ErrAttachLoopbackDevice = errors.New("loopback mounting failed")
|
||||||
|
@ -178,6 +179,14 @@ func (t *Task) GetInfo() (*Info, error) {
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Task) GetDriverVersion() (string, error) {
|
||||||
|
res := DmTaskGetDriverVersion(t.unmanaged)
|
||||||
|
if res == "" {
|
||||||
|
return "", ErrTaskGetDriverVersion
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64,
|
func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64,
|
||||||
length uint64, targetType string, params string) {
|
length uint64, targetType string, params string) {
|
||||||
|
|
||||||
|
@ -394,6 +403,17 @@ func getInfo(name string) (*Info, error) {
|
||||||
return task.GetInfo()
|
return task.GetInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDriverVersion() (string, error) {
|
||||||
|
task := TaskCreate(DeviceVersion)
|
||||||
|
if task == nil {
|
||||||
|
return "", fmt.Errorf("Can't create DeviceVersion task")
|
||||||
|
}
|
||||||
|
if err := task.Run(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return task.GetDriverVersion()
|
||||||
|
}
|
||||||
|
|
||||||
func getStatus(name string) (uint64, uint64, string, string, error) {
|
func getStatus(name string) (uint64, uint64, string, string, error) {
|
||||||
task, err := createTask(DeviceStatus, name)
|
task, err := createTask(DeviceStatus, name)
|
||||||
if task == nil {
|
if task == nil {
|
||||||
|
|
|
@ -85,23 +85,24 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DmGetLibraryVersion = dmGetLibraryVersionFct
|
DmGetLibraryVersion = dmGetLibraryVersionFct
|
||||||
DmGetNextTarget = dmGetNextTargetFct
|
DmGetNextTarget = dmGetNextTargetFct
|
||||||
DmLogInitVerbose = dmLogInitVerboseFct
|
DmLogInitVerbose = dmLogInitVerboseFct
|
||||||
DmSetDevDir = dmSetDevDirFct
|
DmSetDevDir = dmSetDevDirFct
|
||||||
DmTaskAddTarget = dmTaskAddTargetFct
|
DmTaskAddTarget = dmTaskAddTargetFct
|
||||||
DmTaskCreate = dmTaskCreateFct
|
DmTaskCreate = dmTaskCreateFct
|
||||||
DmTaskDestroy = dmTaskDestroyFct
|
DmTaskDestroy = dmTaskDestroyFct
|
||||||
DmTaskGetInfo = dmTaskGetInfoFct
|
DmTaskGetInfo = dmTaskGetInfoFct
|
||||||
DmTaskRun = dmTaskRunFct
|
DmTaskGetDriverVersion = dmTaskGetDriverVersionFct
|
||||||
DmTaskSetAddNode = dmTaskSetAddNodeFct
|
DmTaskRun = dmTaskRunFct
|
||||||
DmTaskSetCookie = dmTaskSetCookieFct
|
DmTaskSetAddNode = dmTaskSetAddNodeFct
|
||||||
DmTaskSetMessage = dmTaskSetMessageFct
|
DmTaskSetCookie = dmTaskSetCookieFct
|
||||||
DmTaskSetName = dmTaskSetNameFct
|
DmTaskSetMessage = dmTaskSetMessageFct
|
||||||
DmTaskSetRo = dmTaskSetRoFct
|
DmTaskSetName = dmTaskSetNameFct
|
||||||
DmTaskSetSector = dmTaskSetSectorFct
|
DmTaskSetRo = dmTaskSetRoFct
|
||||||
DmUdevWait = dmUdevWaitFct
|
DmTaskSetSector = dmTaskSetSectorFct
|
||||||
LogWithErrnoInit = logWithErrnoInitFct
|
DmUdevWait = dmUdevWaitFct
|
||||||
|
LogWithErrnoInit = logWithErrnoInitFct
|
||||||
)
|
)
|
||||||
|
|
||||||
func free(p *C.char) {
|
func free(p *C.char) {
|
||||||
|
@ -184,6 +185,16 @@ func dmTaskGetInfoFct(task *CDmTask, info *Info) int {
|
||||||
return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo))
|
return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dmTaskGetDriverVersionFct(task *CDmTask) string {
|
||||||
|
buffer := C.malloc(128)
|
||||||
|
defer C.free(buffer)
|
||||||
|
res := C.dm_task_get_driver_version((*C.struct_dm_task)(task), (*C.char)(buffer), 128)
|
||||||
|
if res == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return C.GoString((*C.char)(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
|
func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
|
||||||
var (
|
var (
|
||||||
Cstart, Clength C.uint64_t
|
Cstart, Clength C.uint64_t
|
||||||
|
|
|
@ -26,8 +26,8 @@ type Driver struct {
|
||||||
home string
|
home string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(home string) (graphdriver.Driver, error) {
|
func Init(home string, options []string) (graphdriver.Driver, error) {
|
||||||
deviceSet, err := NewDeviceSet(home, true)
|
deviceSet, err := NewDeviceSet(home, true, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
package devmapper
|
package devmapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -27,3 +29,58 @@ func Mounted(mountpoint string) (bool, error) {
|
||||||
parentSt := parent.Sys().(*syscall.Stat_t)
|
parentSt := parent.Sys().(*syscall.Stat_t)
|
||||||
return mntpointSt.Dev != parentSt.Dev, nil
|
return mntpointSt.Dev != parentSt.Dev, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type probeData struct {
|
||||||
|
fsName string
|
||||||
|
magic string
|
||||||
|
offset uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProbeFsType(device string) (string, error) {
|
||||||
|
probes := []probeData{
|
||||||
|
{"btrfs", "_BHRfS_M", 0x10040},
|
||||||
|
{"ext4", "\123\357", 0x438},
|
||||||
|
{"xfs", "XFSB", 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
maxLen := uint64(0)
|
||||||
|
for _, p := range probes {
|
||||||
|
l := p.offset + uint64(len(p.magic))
|
||||||
|
if l > maxLen {
|
||||||
|
maxLen = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(device)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := make([]byte, maxLen)
|
||||||
|
l, err := file.Read(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
if uint64(l) != maxLen {
|
||||||
|
return "", fmt.Errorf("unable to detect filesystem type of %s, short read", device)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range probes {
|
||||||
|
if bytes.Equal([]byte(p.magic), buffer[p.offset:p.offset+uint64(len(p.magic))]) {
|
||||||
|
return p.fsName, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("Unknown filesystem type on %s", device)
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinMountOptions(a, b string) string {
|
||||||
|
if a == "" {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
if b == "" {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return a + "," + b
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ const (
|
||||||
FsMagicAufs = FsMagic(0x61756673)
|
FsMagicAufs = FsMagic(0x61756673)
|
||||||
)
|
)
|
||||||
|
|
||||||
type InitFunc func(root string) (Driver, error)
|
type InitFunc func(root string, options []string) (Driver, error)
|
||||||
|
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
String() string
|
String() string
|
||||||
|
@ -69,23 +69,23 @@ func Register(name string, initFunc InitFunc) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDriver(name, home string) (Driver, error) {
|
func GetDriver(name, home string, options []string) (Driver, error) {
|
||||||
if initFunc, exists := drivers[name]; exists {
|
if initFunc, exists := drivers[name]; exists {
|
||||||
return initFunc(path.Join(home, name))
|
return initFunc(path.Join(home, name), options)
|
||||||
}
|
}
|
||||||
return nil, ErrNotSupported
|
return nil, ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(root string) (driver Driver, err error) {
|
func New(root string, options []string) (driver Driver, err error) {
|
||||||
for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
|
for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
|
||||||
if name != "" {
|
if name != "" {
|
||||||
return GetDriver(name, root)
|
return GetDriver(name, root, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for priority drivers first
|
// Check for priority drivers first
|
||||||
for _, name := range priority {
|
for _, name := range priority {
|
||||||
driver, err = GetDriver(name, root)
|
driver, err = GetDriver(name, root, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
|
if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
|
||||||
continue
|
continue
|
||||||
|
@ -97,7 +97,7 @@ func New(root string) (driver Driver, err error) {
|
||||||
|
|
||||||
// Check all registered drivers if no priority driver is found
|
// Check all registered drivers if no priority driver is found
|
||||||
for _, initFunc := range drivers {
|
for _, initFunc := range drivers {
|
||||||
if driver, err = initFunc(root); err != nil {
|
if driver, err = initFunc(root, options); err != nil {
|
||||||
if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
|
if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ func newDriver(t *testing.T, name string) *Driver {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := graphdriver.GetDriver(name, root)
|
d, err := graphdriver.GetDriver(name, root, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites {
|
if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites {
|
||||||
t.Skip("Driver %s not supported", name)
|
t.Skip("Driver %s not supported", name)
|
||||||
|
|
|
@ -12,7 +12,7 @@ func init() {
|
||||||
graphdriver.Register("vfs", Init)
|
graphdriver.Register("vfs", Init)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(home string) (graphdriver.Driver, error) {
|
func Init(home string, options []string) (graphdriver.Driver, error) {
|
||||||
d := &Driver{
|
d := &Driver{
|
||||||
home: home,
|
home: home,
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ type Config struct {
|
||||||
BridgeIP string
|
BridgeIP string
|
||||||
InterContainerCommunication bool
|
InterContainerCommunication bool
|
||||||
GraphDriver string
|
GraphDriver string
|
||||||
|
GraphOptions []string
|
||||||
ExecDriver string
|
ExecDriver string
|
||||||
Mtu int
|
Mtu int
|
||||||
DisableNetwork bool
|
DisableNetwork bool
|
||||||
|
@ -49,6 +50,10 @@ func ConfigFromJob(job *engine.Job) *Config {
|
||||||
ExecDriver: job.Getenv("ExecDriver"),
|
ExecDriver: job.Getenv("ExecDriver"),
|
||||||
EnableSelinuxSupport: job.GetenvBool("EnableSelinuxSupport"),
|
EnableSelinuxSupport: job.GetenvBool("EnableSelinuxSupport"),
|
||||||
}
|
}
|
||||||
|
if graphOpts := job.GetenvList("GraphOptions"); graphOpts != nil {
|
||||||
|
config.GraphOptions = graphOpts
|
||||||
|
}
|
||||||
|
|
||||||
if dns := job.GetenvList("Dns"); dns != nil {
|
if dns := job.GetenvList("Dns"); dns != nil {
|
||||||
config.Dns = dns
|
config.Dns = dns
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ func main() {
|
||||||
var (
|
var (
|
||||||
flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
|
flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
|
||||||
flDaemon = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
|
flDaemon = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
|
||||||
|
flGraphOpts opts.ListOpts
|
||||||
flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
|
flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
|
||||||
flAutoRestart = flag.Bool([]string{"r", "-restart"}, true, "Restart previously running containers")
|
flAutoRestart = flag.Bool([]string{"r", "-restart"}, true, "Restart previously running containers")
|
||||||
bridgeName = flag.String([]string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
|
bridgeName = flag.String([]string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
|
||||||
|
@ -69,6 +70,7 @@ func main() {
|
||||||
flag.Var(&flDns, []string{"#dns", "-dns"}, "Force docker to use specific DNS servers")
|
flag.Var(&flDns, []string{"#dns", "-dns"}, "Force docker to use specific DNS servers")
|
||||||
flag.Var(&flDnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains")
|
flag.Var(&flDnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains")
|
||||||
flag.Var(&flHosts, []string{"H", "-host"}, "The socket(s) to bind to in daemon mode\nspecified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.")
|
flag.Var(&flHosts, []string{"H", "-host"}, "The socket(s) to bind to in daemon mode\nspecified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.")
|
||||||
|
flag.Var(&flGraphOpts, []string{"-storage-opt"}, "Set storage driver options")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -156,6 +158,7 @@ func main() {
|
||||||
job.Setenv("DefaultIp", *flDefaultIp)
|
job.Setenv("DefaultIp", *flDefaultIp)
|
||||||
job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
|
job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
|
||||||
job.Setenv("GraphDriver", *flGraphDriver)
|
job.Setenv("GraphDriver", *flGraphDriver)
|
||||||
|
job.SetenvList("GraphOptions", flGraphOpts.GetAll())
|
||||||
job.Setenv("ExecDriver", *flExecDriver)
|
job.Setenv("ExecDriver", *flExecDriver)
|
||||||
job.SetenvInt("Mtu", *flMtu)
|
job.SetenvInt("Mtu", *flMtu)
|
||||||
job.SetenvBool("EnableSelinuxSupport", *flSelinuxEnabled)
|
job.SetenvBool("EnableSelinuxSupport", *flSelinuxEnabled)
|
||||||
|
|
|
@ -73,6 +73,7 @@ expect an integer, and they can only be specified once.
|
||||||
-p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file
|
-p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file
|
||||||
-r, --restart=true Restart previously running containers
|
-r, --restart=true Restart previously running containers
|
||||||
-s, --storage-driver="" Force the docker runtime to use a specific storage driver
|
-s, --storage-driver="" Force the docker runtime to use a specific storage driver
|
||||||
|
--storage-opt=[] Set storage driver options
|
||||||
--selinux-enabled=false Enable selinux support
|
--selinux-enabled=false Enable selinux support
|
||||||
--tls=false Use TLS; implied by tls-verify flags
|
--tls=false Use TLS; implied by tls-verify flags
|
||||||
--tlscacert="/home/sven/.docker/ca.pem" Trust only remotes providing a certificate signed by the CA given here
|
--tlscacert="/home/sven/.docker/ca.pem" Trust only remotes providing a certificate signed by the CA given here
|
||||||
|
|
|
@ -36,7 +36,7 @@ func fakeTar() (io.Reader, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkTestTagStore(root string, t *testing.T) *TagStore {
|
func mkTestTagStore(root string, t *testing.T) *TagStore {
|
||||||
driver, err := graphdriver.New(root)
|
driver, err := graphdriver.New(root, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,7 +293,7 @@ func tempGraph(t *testing.T) (*graph.Graph, graphdriver.Driver) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
driver, err := graphdriver.New(tmp)
|
driver, err := graphdriver.New(tmp, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,42 @@ func HumanSize(size int64) string {
|
||||||
return fmt.Sprintf("%.4g %s", sizef, units[i])
|
return fmt.Sprintf("%.4g %s", sizef, units[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromHumanSize returns an integer from a human-readable specification of a size
|
||||||
|
// using SI standard (eg. "44kB", "17MB")
|
||||||
|
func FromHumanSize(size string) (int64, error) {
|
||||||
|
re, error := regexp.Compile("^(\\d+)([kKmMgGtTpP])?[bB]?$")
|
||||||
|
if error != nil {
|
||||||
|
return -1, fmt.Errorf("%s does not specify not a size", size)
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := re.FindStringSubmatch(size)
|
||||||
|
|
||||||
|
if len(matches) != 3 {
|
||||||
|
return -1, fmt.Errorf("Invalid size: '%s'", size)
|
||||||
|
}
|
||||||
|
|
||||||
|
theSize, error := strconv.ParseInt(matches[1], 10, 0)
|
||||||
|
if error != nil {
|
||||||
|
return -1, error
|
||||||
|
}
|
||||||
|
|
||||||
|
unit := strings.ToLower(matches[2])
|
||||||
|
|
||||||
|
if unit == "k" {
|
||||||
|
theSize *= 1000
|
||||||
|
} else if unit == "m" {
|
||||||
|
theSize *= 1000 * 1000
|
||||||
|
} else if unit == "g" {
|
||||||
|
theSize *= 1000 * 1000 * 1000
|
||||||
|
} else if unit == "t" {
|
||||||
|
theSize *= 1000 * 1000 * 1000 * 1000
|
||||||
|
} else if unit == "p" {
|
||||||
|
theSize *= 1000 * 1000 * 1000 * 1000 * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
return theSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Parses a human-readable string representing an amount of RAM
|
// Parses a human-readable string representing an amount of RAM
|
||||||
// in bytes, kibibytes, mebibytes or gibibytes, and returns the
|
// in bytes, kibibytes, mebibytes or gibibytes, and returns the
|
||||||
// number of bytes, or -1 if the string is unparseable.
|
// number of bytes, or -1 if the string is unparseable.
|
||||||
|
|
|
@ -20,6 +20,41 @@ func TestHumanSize(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFromHumanSize(t *testing.T) {
|
||||||
|
assertFromHumanSize(t, "32", false, 32)
|
||||||
|
assertFromHumanSize(t, "32b", false, 32)
|
||||||
|
assertFromHumanSize(t, "32B", false, 32)
|
||||||
|
assertFromHumanSize(t, "32k", false, 32*1000)
|
||||||
|
assertFromHumanSize(t, "32K", false, 32*1000)
|
||||||
|
assertFromHumanSize(t, "32kb", false, 32*1000)
|
||||||
|
assertFromHumanSize(t, "32Kb", false, 32*1000)
|
||||||
|
assertFromHumanSize(t, "32Mb", false, 32*1000*1000)
|
||||||
|
assertFromHumanSize(t, "32Gb", false, 32*1000*1000*1000)
|
||||||
|
assertFromHumanSize(t, "32Tb", false, 32*1000*1000*1000*1000)
|
||||||
|
assertFromHumanSize(t, "8Pb", false, 8*1000*1000*1000*1000*1000)
|
||||||
|
|
||||||
|
assertFromHumanSize(t, "", true, -1)
|
||||||
|
assertFromHumanSize(t, "hello", true, -1)
|
||||||
|
assertFromHumanSize(t, "-32", true, -1)
|
||||||
|
assertFromHumanSize(t, " 32 ", true, -1)
|
||||||
|
assertFromHumanSize(t, "32 mb", true, -1)
|
||||||
|
assertFromHumanSize(t, "32m b", true, -1)
|
||||||
|
assertFromHumanSize(t, "32bm", true, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertFromHumanSize(t *testing.T, size string, expectError bool, expectedBytes int64) {
|
||||||
|
actualBytes, err := FromHumanSize(size)
|
||||||
|
if (err != nil) && !expectError {
|
||||||
|
t.Errorf("Unexpected error parsing '%s': %s", size, err)
|
||||||
|
}
|
||||||
|
if (err == nil) && expectError {
|
||||||
|
t.Errorf("Expected to get an error parsing '%s', but got none (bytes=%d)", size, actualBytes)
|
||||||
|
}
|
||||||
|
if actualBytes != expectedBytes {
|
||||||
|
t.Errorf("Expected '%s' to parse as %d bytes, got %d", size, expectedBytes, actualBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRAMInBytes(t *testing.T) {
|
func TestRAMInBytes(t *testing.T) {
|
||||||
assertRAMInBytes(t, "32", false, 32)
|
assertRAMInBytes(t, "32", false, 32)
|
||||||
assertRAMInBytes(t, "32b", false, 32)
|
assertRAMInBytes(t, "32b", false, 32)
|
||||||
|
|
Loading…
Reference in a new issue