From 13f07b636f014331a5d5dc0eac40d6ac9e5c9263 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 31 Mar 2014 09:30:20 +0200 Subject: [PATCH 1/9] pkg/units: Add FromHumanSize This does the "reverse" of HumanSize, i.e. maps a string to an int64 using SI prefixes for the extension. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- pkg/units/size.go | 36 ++++++++++++++++++++++++++++++++++++ pkg/units/size_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/pkg/units/size.go b/pkg/units/size.go index 99c8800965..480ec2f141 100644 --- a/pkg/units/size.go +++ b/pkg/units/size.go @@ -21,6 +21,42 @@ func HumanSize(size int64) string { 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 // in bytes, kibibytes, mebibytes or gibibytes, and returns the // number of bytes, or -1 if the string is unparseable. diff --git a/pkg/units/size_test.go b/pkg/units/size_test.go index 958a4ca13d..5240bbd9f0 100644 --- a/pkg/units/size_test.go +++ b/pkg/units/size_test.go @@ -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) { assertRAMInBytes(t, "32", false, 32) assertRAMInBytes(t, "32b", false, 32) From 948e54ac455f88c79524dcf117df80f2d4c3f96c Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 27 Mar 2014 17:45:02 +0100 Subject: [PATCH 2/9] devmapper: Fail init with ErrNotSupported if simple devmapper call fails If we can't even get the current device mapper driver version, then we cleanly fail the devmapper driver as not supported and fall back on the next one. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- daemon/graphdriver/devmapper/deviceset.go | 7 +++ daemon/graphdriver/devmapper/devmapper.go | 20 +++++++++ .../devmapper/devmapper_wrapper.go | 45 ++++++++++++------- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 4de7858c1f..f36faca900 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -17,6 +17,7 @@ import ( "syscall" "time" + "github.com/dotcloud/docker/daemon/graphdriver" "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/utils" ) @@ -506,6 +507,12 @@ func (devices *DeviceSet) ResizePool(size int64) error { func (devices *DeviceSet) initDevmapper(doInit bool) error { 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) { return err } diff --git a/daemon/graphdriver/devmapper/devmapper.go b/daemon/graphdriver/devmapper/devmapper.go index d78ae1ef8b..a6602c276e 100644 --- a/daemon/graphdriver/devmapper/devmapper.go +++ b/daemon/graphdriver/devmapper/devmapper.go @@ -52,6 +52,7 @@ var ( ErrTaskAddTarget = errors.New("dm_task_add_target failed") ErrTaskSetSector = errors.New("dm_task_set_sector 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") ErrNilCookie = errors.New("cookie ptr can't be nil") ErrAttachLoopbackDevice = errors.New("loopback mounting failed") @@ -178,6 +179,14 @@ func (t *Task) GetInfo() (*Info, error) { 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, length uint64, targetType string, params string) { @@ -394,6 +403,17 @@ func getInfo(name string) (*Info, error) { 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) { task, err := createTask(DeviceStatus, name) if task == nil { diff --git a/daemon/graphdriver/devmapper/devmapper_wrapper.go b/daemon/graphdriver/devmapper/devmapper_wrapper.go index bf558affc8..9f1b5a6054 100644 --- a/daemon/graphdriver/devmapper/devmapper_wrapper.go +++ b/daemon/graphdriver/devmapper/devmapper_wrapper.go @@ -85,23 +85,24 @@ const ( ) var ( - DmGetLibraryVersion = dmGetLibraryVersionFct - DmGetNextTarget = dmGetNextTargetFct - DmLogInitVerbose = dmLogInitVerboseFct - DmSetDevDir = dmSetDevDirFct - DmTaskAddTarget = dmTaskAddTargetFct - DmTaskCreate = dmTaskCreateFct - DmTaskDestroy = dmTaskDestroyFct - DmTaskGetInfo = dmTaskGetInfoFct - DmTaskRun = dmTaskRunFct - DmTaskSetAddNode = dmTaskSetAddNodeFct - DmTaskSetCookie = dmTaskSetCookieFct - DmTaskSetMessage = dmTaskSetMessageFct - DmTaskSetName = dmTaskSetNameFct - DmTaskSetRo = dmTaskSetRoFct - DmTaskSetSector = dmTaskSetSectorFct - DmUdevWait = dmUdevWaitFct - LogWithErrnoInit = logWithErrnoInitFct + DmGetLibraryVersion = dmGetLibraryVersionFct + DmGetNextTarget = dmGetNextTargetFct + DmLogInitVerbose = dmLogInitVerboseFct + DmSetDevDir = dmSetDevDirFct + DmTaskAddTarget = dmTaskAddTargetFct + DmTaskCreate = dmTaskCreateFct + DmTaskDestroy = dmTaskDestroyFct + DmTaskGetInfo = dmTaskGetInfoFct + DmTaskGetDriverVersion = dmTaskGetDriverVersionFct + DmTaskRun = dmTaskRunFct + DmTaskSetAddNode = dmTaskSetAddNodeFct + DmTaskSetCookie = dmTaskSetCookieFct + DmTaskSetMessage = dmTaskSetMessageFct + DmTaskSetName = dmTaskSetNameFct + DmTaskSetRo = dmTaskSetRoFct + DmTaskSetSector = dmTaskSetSectorFct + DmUdevWait = dmUdevWaitFct + LogWithErrnoInit = logWithErrnoInitFct ) 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)) } +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 { var ( Cstart, Clength C.uint64_t From 822ea97ffcf10645720bb93108a60f8b9ce9931d Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 5 Jun 2014 10:34:20 +0200 Subject: [PATCH 3/9] Add --storage-opt graph driver option and pass through to driver This lets you add storage specific options for the daemon. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- daemon/daemon.go | 4 ++-- daemon/graphdriver/aufs/aufs.go | 2 +- daemon/graphdriver/aufs/aufs_test.go | 2 +- daemon/graphdriver/btrfs/btrfs.go | 2 +- daemon/graphdriver/devmapper/driver.go | 2 +- daemon/graphdriver/driver.go | 14 +++++++------- daemon/graphdriver/graphtest/graphtest.go | 2 +- daemon/graphdriver/vfs/driver.go | 2 +- daemonconfig/config.go | 5 +++++ docker/docker.go | 3 +++ docs/sources/reference/commandline/cli.md | 1 + graph/tags_unit_test.go | 2 +- integration/graph_test.go | 2 +- 13 files changed, 26 insertions(+), 17 deletions(-) diff --git a/daemon/daemon.go b/daemon/daemon.go index 8879d019ec..2c2b046946 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -780,7 +780,7 @@ func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*D graphdriver.DefaultDriver = config.GraphDriver // Load storage driver - driver, err := graphdriver.New(config.Root) + driver, err := graphdriver.New(config.Root, config.GraphOptions) if err != nil { 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 // 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 { return nil, err } diff --git a/daemon/graphdriver/aufs/aufs.go b/daemon/graphdriver/aufs/aufs.go index 79de4cda79..43c3128271 100644 --- a/daemon/graphdriver/aufs/aufs.go +++ b/daemon/graphdriver/aufs/aufs.go @@ -57,7 +57,7 @@ type Driver struct { // New returns a new AUFS driver. // 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 if err := supportsAufs(); err != nil { return nil, graphdriver.ErrNotSupported diff --git a/daemon/graphdriver/aufs/aufs_test.go b/daemon/graphdriver/aufs/aufs_test.go index dab5aecc41..b3bad410a5 100644 --- a/daemon/graphdriver/aufs/aufs_test.go +++ b/daemon/graphdriver/aufs/aufs_test.go @@ -17,7 +17,7 @@ var ( ) func testInit(dir string, t *testing.T) graphdriver.Driver { - d, err := Init(dir) + d, err := Init(dir, nil) if err != nil { if err == graphdriver.ErrNotSupported { t.Skip(err) diff --git a/daemon/graphdriver/btrfs/btrfs.go b/daemon/graphdriver/btrfs/btrfs.go index 56299b18ee..ba3ecba761 100644 --- a/daemon/graphdriver/btrfs/btrfs.go +++ b/daemon/graphdriver/btrfs/btrfs.go @@ -22,7 +22,7 @@ func 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) var buf syscall.Statfs_t diff --git a/daemon/graphdriver/devmapper/driver.go b/daemon/graphdriver/devmapper/driver.go index 609971cda1..50bda1cd2e 100644 --- a/daemon/graphdriver/devmapper/driver.go +++ b/daemon/graphdriver/devmapper/driver.go @@ -26,7 +26,7 @@ type Driver struct { home string } -func Init(home string) (graphdriver.Driver, error) { +func Init(home string, options []string) (graphdriver.Driver, error) { deviceSet, err := NewDeviceSet(home, true) if err != nil { return nil, err diff --git a/daemon/graphdriver/driver.go b/daemon/graphdriver/driver.go index 8f9c0b6d2b..93d4ed2535 100644 --- a/daemon/graphdriver/driver.go +++ b/daemon/graphdriver/driver.go @@ -15,7 +15,7 @@ const ( FsMagicAufs = FsMagic(0x61756673) ) -type InitFunc func(root string) (Driver, error) +type InitFunc func(root string, options []string) (Driver, error) type Driver interface { String() string @@ -69,23 +69,23 @@ func Register(name string, initFunc InitFunc) error { return nil } -func GetDriver(name, home string) (Driver, error) { +func GetDriver(name, home string, options []string) (Driver, error) { if initFunc, exists := drivers[name]; exists { - return initFunc(path.Join(home, name)) + return initFunc(path.Join(home, name), options) } 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} { if name != "" { - return GetDriver(name, root) + return GetDriver(name, root, options) } } // Check for priority drivers first for _, name := range priority { - driver, err = GetDriver(name, root) + driver, err = GetDriver(name, root, options) if err != nil { if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS { continue @@ -97,7 +97,7 @@ func New(root string) (driver Driver, err error) { // Check all registered drivers if no priority driver is found 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 { continue } diff --git a/daemon/graphdriver/graphtest/graphtest.go b/daemon/graphdriver/graphtest/graphtest.go index 56ea8f7d42..d53878c45a 100644 --- a/daemon/graphdriver/graphtest/graphtest.go +++ b/daemon/graphdriver/graphtest/graphtest.go @@ -29,7 +29,7 @@ func newDriver(t *testing.T, name string) *Driver { t.Fatal(err) } - d, err := graphdriver.GetDriver(name, root) + d, err := graphdriver.GetDriver(name, root, nil) if err != nil { if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites { t.Skip("Driver %s not supported", name) diff --git a/daemon/graphdriver/vfs/driver.go b/daemon/graphdriver/vfs/driver.go index 7473aa659d..992af0e149 100644 --- a/daemon/graphdriver/vfs/driver.go +++ b/daemon/graphdriver/vfs/driver.go @@ -12,7 +12,7 @@ func init() { graphdriver.Register("vfs", Init) } -func Init(home string) (graphdriver.Driver, error) { +func Init(home string, options []string) (graphdriver.Driver, error) { d := &Driver{ home: home, } diff --git a/daemonconfig/config.go b/daemonconfig/config.go index 619bfe582f..9f77d84a58 100644 --- a/daemonconfig/config.go +++ b/daemonconfig/config.go @@ -25,6 +25,7 @@ type Config struct { BridgeIP string InterContainerCommunication bool GraphDriver string + GraphOptions []string ExecDriver string Mtu int DisableNetwork bool @@ -49,6 +50,10 @@ func ConfigFromJob(job *engine.Job) *Config { ExecDriver: job.Getenv("ExecDriver"), EnableSelinuxSupport: job.GetenvBool("EnableSelinuxSupport"), } + if graphOpts := job.GetenvList("GraphOptions"); graphOpts != nil { + config.GraphOptions = graphOpts + } + if dns := job.GetenvList("Dns"); dns != nil { config.Dns = dns } diff --git a/docker/docker.go b/docker/docker.go index 4215ed3a95..56bcb04e41 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -41,6 +41,7 @@ func main() { var ( flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit") flDaemon = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode") + flGraphOpts opts.ListOpts flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") 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") @@ -69,6 +70,7 @@ func main() { 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(&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() @@ -156,6 +158,7 @@ func main() { job.Setenv("DefaultIp", *flDefaultIp) job.SetenvBool("InterContainerCommunication", *flInterContainerComm) job.Setenv("GraphDriver", *flGraphDriver) + job.SetenvList("GraphOptions", flGraphOpts.GetAll()) job.Setenv("ExecDriver", *flExecDriver) job.SetenvInt("Mtu", *flMtu) job.SetenvBool("EnableSelinuxSupport", *flSelinuxEnabled) diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 16190ab1de..fd5d140a00 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -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 -r, --restart=true Restart previously running containers -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 --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 diff --git a/graph/tags_unit_test.go b/graph/tags_unit_test.go index bc438131ca..42e097724d 100644 --- a/graph/tags_unit_test.go +++ b/graph/tags_unit_test.go @@ -36,7 +36,7 @@ func fakeTar() (io.Reader, error) { } func mkTestTagStore(root string, t *testing.T) *TagStore { - driver, err := graphdriver.New(root) + driver, err := graphdriver.New(root, nil) if err != nil { t.Fatal(err) } diff --git a/integration/graph_test.go b/integration/graph_test.go index c29055edfc..dc056f7e1c 100644 --- a/integration/graph_test.go +++ b/integration/graph_test.go @@ -293,7 +293,7 @@ func tempGraph(t *testing.T) (*graph.Graph, graphdriver.Driver) { if err != nil { t.Fatal(err) } - driver, err := graphdriver.New(tmp) + driver, err := graphdriver.New(tmp, nil) if err != nil { t.Fatal(err) } From 7f5ba068f438ee159bfca7396de4987bcae45809 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 27 Mar 2014 17:48:32 +0100 Subject: [PATCH 4/9] devmapper: Add --storage-opt options for basic devicemapper settings This allows setting these settings to be passed: dm.basesize dm.loopdatasize dm.loopmetadatasize Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- daemon/graphdriver/devmapper/README.md | 73 +++++++++++++++++++++++ daemon/graphdriver/devmapper/deviceset.go | 54 ++++++++++++++--- daemon/graphdriver/devmapper/driver.go | 2 +- 3 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 daemon/graphdriver/devmapper/README.md diff --git a/daemon/graphdriver/devmapper/README.md b/daemon/graphdriver/devmapper/README.md new file mode 100644 index 0000000000..f2c855c893 --- /dev/null +++ b/daemon/graphdriver/devmapper/README.md @@ -0,0 +1,73 @@ +## 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 locaion (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`` + diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index f36faca900..430f5c2e60 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -13,12 +13,14 @@ import ( "path" "path/filepath" "strconv" + "strings" "sync" "syscall" "time" "github.com/dotcloud/docker/daemon/graphdriver" "github.com/dotcloud/docker/pkg/label" + "github.com/dotcloud/docker/pkg/units" "github.com/dotcloud/docker/utils" ) @@ -65,6 +67,11 @@ type DeviceSet struct { TransactionId uint64 NewTransactionId uint64 nextDeviceId int + + // Options + dataLoopbackSize int64 + metaDataLoopbackSize int64 + baseFsSize uint64 } type DiskUsage struct { @@ -378,8 +385,8 @@ func (devices *DeviceSet) setupBaseImage() error { // Ids are 24bit, so wrap around devices.nextDeviceId = (id + 1) & 0xffffff - utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize) - info, err := devices.registerDevice(id, "", DefaultBaseFsSize) + utils.Debugf("Registering base device (id %v) with FS size %v", id, devices.baseFsSize) + info, err := devices.registerDevice(id, "", devices.baseFsSize) if err != nil { _ = deleteDevice(devices.getPoolDevName(), id) utils.Debugf("\n--->Err: %s\n", err) @@ -567,12 +574,12 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { } createdLoopback = !hasData || !hasMetadata - data, err := devices.ensureImage("data", DefaultDataLoopbackSize) + data, err := devices.ensureImage("data", devices.dataLoopbackSize) if err != nil { utils.Debugf("Error device ensureImage (data): %s\n", err) return err } - metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize) + metadata, err := devices.ensureImage("metadata", devices.metaDataLoopbackSize) if err != nil { utils.Debugf("Error device ensureImage (metadata): %s\n", err) return err @@ -1091,12 +1098,45 @@ func (devices *DeviceSet) Status() *Status { return status } -func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) { +func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error) { SetDevDir("/dev") devices := &DeviceSet{ - root: root, - MetaData: MetaData{Devices: make(map[string]*DevInfo)}, + root: root, + MetaData: MetaData{Devices: make(map[string]*DevInfo)}, + dataLoopbackSize: DefaultDataLoopbackSize, + metaDataLoopbackSize: DefaultMetaDataLoopbackSize, + baseFsSize: DefaultBaseFsSize, + } + + 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 + default: + return nil, fmt.Errorf("Unknown option %s\n", key) + } } if err := devices.initDevmapper(doInit); err != nil { diff --git a/daemon/graphdriver/devmapper/driver.go b/daemon/graphdriver/devmapper/driver.go index 50bda1cd2e..5bfd8ee658 100644 --- a/daemon/graphdriver/devmapper/driver.go +++ b/daemon/graphdriver/devmapper/driver.go @@ -27,7 +27,7 @@ type Driver struct { } func Init(home string, options []string) (graphdriver.Driver, error) { - deviceSet, err := NewDeviceSet(home, true) + deviceSet, err := NewDeviceSet(home, true, options) if err != nil { return nil, err } From 10083f414017636065aa50610f07784738df8e7a Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 18 Mar 2014 12:26:42 +0100 Subject: [PATCH 5/9] devicemapper: Probe what filesystem to use when mounting Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- daemon/graphdriver/devmapper/deviceset.go | 9 ++++- daemon/graphdriver/devmapper/mount.go | 47 +++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 430f5c2e60..5e055f6dba 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -921,11 +921,16 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error { var flags uintptr = syscall.MS_MGC_VAL + fstype, err := ProbeFsType(info.DevName()) + if err != nil { + return err + } + mountOptions := label.FormatMountLabel("discard", mountLabel) - err = syscall.Mount(info.DevName(), path, "ext4", flags, mountOptions) + err = syscall.Mount(info.DevName(), path, fstype, flags, mountOptions) if err != nil && err == syscall.EINVAL { mountOptions = label.FormatMountLabel("", mountLabel) - err = syscall.Mount(info.DevName(), path, "ext4", flags, mountOptions) + err = syscall.Mount(info.DevName(), path, fstype, flags, mountOptions) } if err != nil { return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err) diff --git a/daemon/graphdriver/devmapper/mount.go b/daemon/graphdriver/devmapper/mount.go index 6de9e46c8c..4ec0698c6c 100644 --- a/daemon/graphdriver/devmapper/mount.go +++ b/daemon/graphdriver/devmapper/mount.go @@ -3,6 +3,8 @@ package devmapper import ( + "bytes" + "fmt" "os" "path/filepath" "syscall" @@ -27,3 +29,48 @@ func Mounted(mountpoint string) (bool, error) { parentSt := parent.Sys().(*syscall.Stat_t) 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) +} From 807bc2cd049d97f31eab54ce3d5719d63240e3e7 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 18 Mar 2014 14:23:43 +0100 Subject: [PATCH 6/9] devmapper: Allow specifying filesystem for thin devices This adds the following --storage-opts for the daemon: dm.fs: The filesystem to use for the base image dm.mkfsarg: Add an argument to the mkfs command for the base image dm.mountopt: Add a mount option for devicemapper mount Currently supported filesystems are xfs and ext4. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- daemon/graphdriver/devmapper/README.md | 24 +++++++++++ daemon/graphdriver/devmapper/deviceset.go | 51 +++++++++++++++++++---- daemon/graphdriver/devmapper/mount.go | 10 +++++ 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/daemon/graphdriver/devmapper/README.md b/daemon/graphdriver/devmapper/README.md index f2c855c893..4d32a14355 100644 --- a/daemon/graphdriver/devmapper/README.md +++ b/daemon/graphdriver/devmapper/README.md @@ -71,3 +71,27 @@ Here is the list of supported options: ``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`` diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 5e055f6dba..13f68f61e3 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -72,6 +72,9 @@ type DeviceSet struct { dataLoopbackSize int64 metaDataLoopbackSize int64 baseFsSize uint64 + filesystem string + mountOptions string + mkfsArgs []string } type DiskUsage struct { @@ -281,14 +284,30 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *DevInfo) error { func (devices *DeviceSet) createFilesystem(info *DevInfo) error { devname := info.DevName() - err := exec.Command("mkfs.ext4", "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0", devname).Run() - if err != nil { - err = exec.Command("mkfs.ext4", "-E", "nodiscard,lazy_itable_init=0", devname).Run() + args := []string{} + for _, arg := range devices.mkfsArgs { + 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 { utils.Debugf("\n--->Err: %s\n", err) return err } + return nil } @@ -926,11 +945,19 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error { return err } - mountOptions := label.FormatMountLabel("discard", mountLabel) - err = syscall.Mount(info.DevName(), path, fstype, flags, mountOptions) + 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 { - mountOptions = label.FormatMountLabel("", mountLabel) - err = syscall.Mount(info.DevName(), path, fstype, flags, mountOptions) + err = syscall.Mount(info.DevName(), path, fstype, flags, options) } if err != nil { return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err) @@ -1112,6 +1139,7 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error dataLoopbackSize: DefaultDataLoopbackSize, metaDataLoopbackSize: DefaultMetaDataLoopbackSize, baseFsSize: DefaultBaseFsSize, + filesystem: "ext4", } for _, option := range options { @@ -1139,6 +1167,15 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error 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) default: return nil, fmt.Errorf("Unknown option %s\n", key) } diff --git a/daemon/graphdriver/devmapper/mount.go b/daemon/graphdriver/devmapper/mount.go index 4ec0698c6c..c9ff216d5d 100644 --- a/daemon/graphdriver/devmapper/mount.go +++ b/daemon/graphdriver/devmapper/mount.go @@ -74,3 +74,13 @@ func ProbeFsType(device string) (string, error) { 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 +} From a226168a8b877d632cb87c95dd0288f6092b9d8f Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 28 Mar 2014 15:36:55 +0100 Subject: [PATCH 7/9] devmapper: Add options for specifying block devices This adds dm.datadev and dm.metadatadev options that you can use with --storage-opt to set to specific devices to use for the thin provisioning pool. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- daemon/graphdriver/devmapper/README.md | 30 +++++++ daemon/graphdriver/devmapper/deviceset.go | 96 ++++++++++++++++------- 2 files changed, 97 insertions(+), 29 deletions(-) diff --git a/daemon/graphdriver/devmapper/README.md b/daemon/graphdriver/devmapper/README.md index 4d32a14355..53b7174dba 100644 --- a/daemon/graphdriver/devmapper/README.md +++ b/daemon/graphdriver/devmapper/README.md @@ -95,3 +95,33 @@ Here is the list of supported options: 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`` diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 13f68f61e3..497e1102c2 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -75,6 +75,8 @@ type DeviceSet struct { filesystem string mountOptions string mkfsArgs []string + dataDevice string + metadataDevice string } type DiskUsage struct { @@ -581,42 +583,74 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { if info.Exists == 0 { utils.Debugf("Pool doesn't exist. Creating it.") - hasData := devices.hasImage("data") - hasMetadata := devices.hasImage("metadata") + var ( + dataFile *os.File + metadataFile *os.File + ) - if !doInit && !hasData { - return errors.New("Loopback data file not found") + if devices.dataDevice == "" { + // Make sure the sparse images exist in /devicemapper/data + + hasData := devices.hasImage("data") + + if !doInit && !hasData { + return errors.New("Loopback data file not found") + } + + if !hasData { + createdLoopback = true + } + + 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 { + utils.Debugf("\n--->Err: %s\n", err) + return err + } + defer dataFile.Close() + } else { + dataFile, err = os.OpenFile(devices.dataDevice, os.O_RDWR, 0600) + if err != nil { + return err + } } - if !doInit && !hasMetadata { - return errors.New("Loopback metadata file not found") - } + if devices.metadataDevice == "" { + // Make sure the sparse images exist in /devicemapper/metadata - createdLoopback = !hasData || !hasMetadata - data, err := devices.ensureImage("data", devices.dataLoopbackSize) - if err != nil { - utils.Debugf("Error device ensureImage (data): %s\n", err) - return err - } - metadata, err := devices.ensureImage("metadata", devices.metaDataLoopbackSize) - if err != nil { - utils.Debugf("Error device ensureImage (metadata): %s\n", err) - return err - } + hasMetadata := devices.hasImage("metadata") - dataFile, err := attachLoopDevice(data) - if err != nil { - utils.Debugf("\n--->Err: %s\n", err) - return err - } - defer dataFile.Close() + if !doInit && !hasMetadata { + return errors.New("Loopback metadata file not found") + } - metadataFile, err := attachLoopDevice(metadata) - if err != nil { - utils.Debugf("\n--->Err: %s\n", err) - return err + 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 { + utils.Debugf("\n--->Err: %s\n", err) + return err + } + defer metadataFile.Close() + } else { + metadataFile, err = os.OpenFile(devices.metadataDevice, os.O_RDWR, 0600) + if err != nil { + return err + } } - defer metadataFile.Close() if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil { utils.Debugf("\n--->Err: %s\n", err) @@ -1176,6 +1210,10 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error 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 default: return nil, fmt.Errorf("Unknown option %s\n", key) } From 0434a2ce64c0ce07e97e9a516cef226be67d5f5b Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Apr 2014 15:41:25 +0200 Subject: [PATCH 8/9] devmapper: Add blkdiscard option and disable it on raw devices The blkdiscard hack we do on container/image delete is pretty slow, but required to restore space to the "host" root filesystem. However, it is pretty useless on raw devices, and you may not need it in development either. In a simple test of the devicemapper backend on loopback the time to delete 20 container went from 11 seconds to 0.4 seconds with --storage-opt blkdiscard=false. Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- daemon/graphdriver/devmapper/README.md | 16 +++++++++++++ daemon/graphdriver/devmapper/deviceset.go | 28 ++++++++++++++++++----- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/daemon/graphdriver/devmapper/README.md b/daemon/graphdriver/devmapper/README.md index 53b7174dba..ccff3f021b 100644 --- a/daemon/graphdriver/devmapper/README.md +++ b/daemon/graphdriver/devmapper/README.md @@ -125,3 +125,19 @@ Here is the list of supported options: 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`` diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 497e1102c2..a930bf2b96 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -77,6 +77,7 @@ type DeviceSet struct { mkfsArgs []string dataDevice string metadataDevice string + doBlkDiscard bool } type DiskUsage struct { @@ -713,12 +714,14 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error { } func (devices *DeviceSet) deleteDevice(info *DevInfo) error { - // This is a workaround for the kernel not discarding block so - // on the thin pool when we remove a thinp device, so we do it - // manually - if err := devices.activateDeviceIfNeeded(info); err == nil { - if err := BlockDeviceDiscard(info.DevName()); err != nil { - utils.Debugf("Error discarding block on device: %s (ignoring)\n", err) + if devices.doBlkDiscard { + // This is a workaround for the kernel not discarding block so + // on the thin pool when we remove a thinp device, so we do it + // manually + if err := devices.activateDeviceIfNeeded(info); err == nil { + if err := BlockDeviceDiscard(info.DevName()); err != nil { + utils.Debugf("Error discarding block on device: %s (ignoring)\n", err) + } } } @@ -1174,8 +1177,10 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error metaDataLoopbackSize: DefaultMetaDataLoopbackSize, baseFsSize: DefaultBaseFsSize, filesystem: "ext4", + doBlkDiscard: true, } + foundBlkDiscard := false for _, option := range options { key, val, err := utils.ParseKeyValueOpt(option) if err != nil { @@ -1214,11 +1219,22 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error 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 { return nil, err } From b1ac791d8426cd7b6fb5a19f5e918b26c83d83f6 Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Thu, 5 Jun 2014 15:13:43 -0700 Subject: [PATCH 9/9] Ensures files get closed properly. Closes #6213 Docker-DCO-1.1-Signed-off-by: Tibor Vass (github: tiborvass) --- daemon/graphdriver/devmapper/README.md | 2 +- daemon/graphdriver/devmapper/deviceset.go | 23 ++--------------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/daemon/graphdriver/devmapper/README.md b/daemon/graphdriver/devmapper/README.md index ccff3f021b..c8ab1d1ee1 100644 --- a/daemon/graphdriver/devmapper/README.md +++ b/daemon/graphdriver/devmapper/README.md @@ -4,7 +4,7 @@ The device mapper graphdriver uses the device mapper thin provisioning module (dm-thinp) to implement CoW snapshots. For each devicemapper -graph locaion (typically `/var/lib/docker/devicemapper`, $graph below) +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 diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index a930bf2b96..48323f6610 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -307,7 +307,6 @@ func (devices *DeviceSet) createFilesystem(info *DevInfo) error { err = fmt.Errorf("Unsupported filesystem type %s", devices.filesystem) } if err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } @@ -317,12 +316,10 @@ func (devices *DeviceSet) createFilesystem(info *DevInfo) error { func (devices *DeviceSet) initMetaData() error { _, _, _, params, err := getStatus(devices.getPoolName()) if err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } devices.NewTransactionId = devices.TransactionId @@ -331,7 +328,6 @@ func (devices *DeviceSet) initMetaData() error { jsonData, err := ioutil.ReadFile(devices.oldMetadataFile()) if err != nil && !os.IsNotExist(err) { - utils.Debugf("\n--->Err: %s\n", err) return err } @@ -339,7 +335,6 @@ func (devices *DeviceSet) initMetaData() error { m := MetaData{Devices: make(map[string]*DevInfo)} if err := json.Unmarshal(jsonData, &m); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } @@ -389,7 +384,6 @@ func (devices *DeviceSet) setupBaseImage() error { if oldInfo != nil && !oldInfo.Initialized { utils.Debugf("Removing uninitialized base image") if err := devices.deleteDevice(oldInfo); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } } @@ -400,7 +394,6 @@ func (devices *DeviceSet) setupBaseImage() error { // Create initial device if err := createDevice(devices.getPoolDevName(), &id); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } @@ -411,26 +404,22 @@ func (devices *DeviceSet) setupBaseImage() error { info, err := devices.registerDevice(id, "", devices.baseFsSize) if err != nil { _ = deleteDevice(devices.getPoolDevName(), id) - utils.Debugf("\n--->Err: %s\n", err) return err } utils.Debugf("Creating filesystem on base device-manager snapshot") if err = devices.activateDeviceIfNeeded(info); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } if err := devices.createFilesystem(info); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } info.Initialized = true if err = devices.saveMetadata(info); err != nil { info.Initialized = false - utils.Debugf("\n--->Err: %s\n", err) return err } @@ -610,16 +599,15 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { dataFile, err = attachLoopDevice(data) if err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } - defer dataFile.Close() } else { dataFile, err = os.OpenFile(devices.dataDevice, os.O_RDWR, 0600) if err != nil { return err } } + defer dataFile.Close() if devices.metadataDevice == "" { // Make sure the sparse images exist in /devicemapper/metadata @@ -642,19 +630,17 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { metadataFile, err = attachLoopDevice(metadata) if err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } - defer metadataFile.Close() } else { metadataFile, err = os.OpenFile(devices.metadataDevice, os.O_RDWR, 0600) if err != nil { return err } } + defer metadataFile.Close() if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } } @@ -663,7 +649,6 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { // load the transaction id and migrate old metadata if !createdLoopback { if err = devices.initMetaData(); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } } @@ -775,7 +760,6 @@ func (devices *DeviceSet) deactivatePool() error { devname := devices.getPoolDevName() devinfo, err := getInfo(devname) if err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } if devinfo.Exists != 0 { @@ -797,12 +781,10 @@ func (devices *DeviceSet) deactivateDevice(info *DevInfo) error { devinfo, err := getInfo(info.Name()) if err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } if devinfo.Exists != 0 { if err := devices.removeDeviceAndWait(info.Name()); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } } @@ -1032,7 +1014,6 @@ func (devices *DeviceSet) UnmountDevice(hash string) error { utils.Debugf("[devmapper] Unmount(%s)", info.mountPath) if err := syscall.Unmount(info.mountPath, 0); err != nil { - utils.Debugf("\n--->Err: %s\n", err) return err } utils.Debugf("[devmapper] Unmount done")