From 495a0f953c74577d81cafb97702d688332dd3e55 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 20 Nov 2013 20:05:10 +0000 Subject: [PATCH 01/11] Mock calls to system functions to facilitate unit testing Upstream-commit: 92f94f06ae2fb4cddace5b817d6a6e326f3c9b33 Component: engine --- .../engine/graphdriver/devmapper/deviceset.go | 10 +++++----- .../engine/graphdriver/devmapper/devmapper.go | 2 +- .../graphdriver/devmapper/devmapper_wrapper.go | 8 ++++---- components/engine/graphdriver/devmapper/sys.go | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 components/engine/graphdriver/devmapper/sys.go diff --git a/components/engine/graphdriver/devmapper/deviceset.go b/components/engine/graphdriver/devmapper/deviceset.go index e24840634d..66e44e3da0 100644 --- a/components/engine/graphdriver/devmapper/deviceset.go +++ b/components/engine/graphdriver/devmapper/deviceset.go @@ -344,7 +344,7 @@ func setCloseOnExec(name string) { if link == name { fd, err := strconv.Atoi(i.Name()) if err == nil { - syscall.CloseOnExec(fd) + SyscallCloseOnExec(fd) } } } @@ -716,7 +716,7 @@ func (devices *DeviceSet) Shutdown() error { for path, count := range devices.activeMounts { for i := count; i > 0; i-- { - if err := syscall.Unmount(path, 0); err != nil { + if err := SyscallUnmount(path, 0); err != nil { utils.Debugf("Shutdown unmounting %s, error: %s\n", path, err) } } @@ -758,9 +758,9 @@ func (devices *DeviceSet) MountDevice(hash, path string, readOnly bool) error { flags = flags | syscall.MS_RDONLY } - err := syscall.Mount(info.DevName(), path, "ext4", flags, "discard") + err := SyscallMount(info.DevName(), path, "ext4", flags, "discard") if err != nil && err == syscall.EINVAL { - err = syscall.Mount(info.DevName(), path, "ext4", flags, "") + err = SyscallMount(info.DevName(), path, "ext4", flags, "") } if err != nil { return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err) @@ -779,7 +779,7 @@ func (devices *DeviceSet) UnmountDevice(hash, path string, deactivate bool) erro defer devices.Unlock() utils.Debugf("[devmapper] Unmount(%s)", path) - if err := syscall.Unmount(path, 0); err != nil { + if err := SyscallUnmount(path, 0); err != nil { utils.Debugf("\n--->Err: %s\n", err) return err } diff --git a/components/engine/graphdriver/devmapper/devmapper.go b/components/engine/graphdriver/devmapper/devmapper.go index 6901ba9a6e..56130bd01c 100644 --- a/components/engine/graphdriver/devmapper/devmapper.go +++ b/components/engine/graphdriver/devmapper/devmapper.go @@ -216,7 +216,7 @@ func FindLoopDeviceFor(file *os.File) *os.File { for i := 0; true; i++ { path := fmt.Sprintf("/dev/loop%d", i) - file, err := os.OpenFile(path, os.O_RDWR, 0) + file, err := OSOpenFile(path, os.O_RDWR, 0) if err != nil { if os.IsNotExist(err) { return nil diff --git a/components/engine/graphdriver/devmapper/devmapper_wrapper.go b/components/engine/graphdriver/devmapper/devmapper_wrapper.go index f9a7d039cf..e261db4351 100644 --- a/components/engine/graphdriver/devmapper/devmapper_wrapper.go +++ b/components/engine/graphdriver/devmapper/devmapper_wrapper.go @@ -241,19 +241,19 @@ func dmTaskAddTargetFct(task *CDmTask, func dmGetLoopbackBackingFile(fd uintptr) (uint64, uint64, syscall.Errno) { var lo64 C.struct_loop_info64 - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.LOOP_GET_STATUS64, + _, _, err := SyscallSyscall(syscall.SYS_IOCTL, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) return uint64(lo64.lo_device), uint64(lo64.lo_inode), err } func dmLoopbackSetCapacity(fd uintptr) syscall.Errno { - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.LOOP_SET_CAPACITY, 0) + _, _, err := SyscallSyscall(syscall.SYS_IOCTL, fd, C.LOOP_SET_CAPACITY, 0) return err } func dmGetBlockSizeFct(fd uintptr) (int64, syscall.Errno) { var size int64 - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, + _, _, err := SyscallSyscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) return size, err } @@ -308,7 +308,7 @@ func dmAttachLoopDeviceFct(filename string, fd *int) string { } func getBlockSizeFct(fd uintptr, size *uint64) syscall.Errno { - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, + _, _, err := SyscallSyscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) return err } diff --git a/components/engine/graphdriver/devmapper/sys.go b/components/engine/graphdriver/devmapper/sys.go new file mode 100644 index 0000000000..c2ba1263f0 --- /dev/null +++ b/components/engine/graphdriver/devmapper/sys.go @@ -0,0 +1,15 @@ +package devmapper + + +import ( + "syscall" +) + + +var ( + SyscallMount = syscall.Mount + SyscallUnmount = syscall.Unmount + SyscallCloseOnExec = syscall.CloseOnExec + SyscallSyscall = syscall.Syscall + OSOpenFile = os.OpenFile +) From 8380e89daf734506e9dd9c1f8213384e475f8a86 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 20 Nov 2013 12:49:01 -0800 Subject: [PATCH 02/11] Remove all syscall calls from devicemapper Upstream-commit: 5690139785fc2bfaa2f233ed41d5f927a8b28dbf Component: engine --- .../engine/graphdriver/devmapper/deviceset.go | 26 +++++++-------- .../engine/graphdriver/devmapper/devmapper.go | 11 +++---- .../graphdriver/devmapper/devmapper_test.go | 3 +- .../devmapper/devmapper_wrapper.go | 32 ++++++++----------- .../engine/graphdriver/devmapper/mount.go | 5 ++- .../engine/graphdriver/devmapper/sys.go | 28 ++++++++++++---- 6 files changed, 57 insertions(+), 48 deletions(-) diff --git a/components/engine/graphdriver/devmapper/deviceset.go b/components/engine/graphdriver/devmapper/deviceset.go index 66e44e3da0..ce66211cab 100644 --- a/components/engine/graphdriver/devmapper/deviceset.go +++ b/components/engine/graphdriver/devmapper/deviceset.go @@ -12,7 +12,6 @@ import ( "path/filepath" "strconv" "sync" - "syscall" "time" ) @@ -344,7 +343,7 @@ func setCloseOnExec(name string) { if link == name { fd, err := strconv.Atoi(i.Name()) if err == nil { - SyscallCloseOnExec(fd) + sysCloseOnExec(fd) } } } @@ -468,7 +467,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { if err != nil { return fmt.Errorf("Error looking up dir %s: %s", devices.root, err) } - sysSt := st.Sys().(*syscall.Stat_t) + sysSt := toSysStatT(st.Sys()) // "reg-" stands for "regular file". // In the future we might use "dev-" for "device file", etc. // docker-maj,min[-inode] stands for: @@ -708,15 +707,16 @@ func (devices *DeviceSet) byHash(hash string) (devname string, err error) { } func (devices *DeviceSet) Shutdown() error { - utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix) - defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix) devices.Lock() - utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root) defer devices.Unlock() + utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix) + utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root) + defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix) + for path, count := range devices.activeMounts { for i := count; i > 0; i-- { - if err := SyscallUnmount(path, 0); err != nil { + if err := sysUnmount(path, 0); err != nil { utils.Debugf("Shutdown unmounting %s, error: %s\n", path, err) } } @@ -752,15 +752,15 @@ func (devices *DeviceSet) MountDevice(hash, path string, readOnly bool) error { info := devices.Devices[hash] - var flags uintptr = syscall.MS_MGC_VAL + var flags uintptr = sysMsMgcVal if readOnly { - flags = flags | syscall.MS_RDONLY + flags = flags | sysMsRdOnly } - err := SyscallMount(info.DevName(), path, "ext4", flags, "discard") - if err != nil && err == syscall.EINVAL { - err = SyscallMount(info.DevName(), path, "ext4", flags, "") + err := sysMount(info.DevName(), path, "ext4", flags, "discard") + if err != nil && err == sysEInval { + err = sysMount(info.DevName(), path, "ext4", flags, "") } if err != nil { return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err) @@ -779,7 +779,7 @@ func (devices *DeviceSet) UnmountDevice(hash, path string, deactivate bool) erro defer devices.Unlock() utils.Debugf("[devmapper] Unmount(%s)", path) - if err := SyscallUnmount(path, 0); err != nil { + if err := sysUnmount(path, 0); err != nil { utils.Debugf("\n--->Err: %s\n", err) return err } diff --git a/components/engine/graphdriver/devmapper/devmapper.go b/components/engine/graphdriver/devmapper/devmapper.go index 56130bd01c..cccb168f95 100644 --- a/components/engine/graphdriver/devmapper/devmapper.go +++ b/components/engine/graphdriver/devmapper/devmapper.go @@ -6,7 +6,6 @@ import ( "github.com/dotcloud/docker/utils" "os" "runtime" - "syscall" ) type DevmapperLogger interface { @@ -210,13 +209,13 @@ func FindLoopDeviceFor(file *os.File) *os.File { if err != nil { return nil } - targetInode := stat.Sys().(*syscall.Stat_t).Ino - targetDevice := stat.Sys().(*syscall.Stat_t).Dev + targetInode := stat.Sys().(*sysStatT).Ino + targetDevice := stat.Sys().(*sysStatT).Dev for i := 0; true; i++ { path := fmt.Sprintf("/dev/loop%d", i) - file, err := OSOpenFile(path, os.O_RDWR, 0) + file, err := osOpenFile(path, os.O_RDWR, 0) if err != nil { if os.IsNotExist(err) { return nil @@ -394,8 +393,8 @@ func getStatus(name string) (uint64, uint64, string, string, error) { return 0, 0, "", "", fmt.Errorf("Non existing device %s", name) } - _, start, length, target_type, params := task.GetNextTarget(0) - return start, length, target_type, params, nil + _, start, length, targetType, params := task.GetNextTarget(0) + return start, length, targetType, params, nil } func setTransactionId(poolName string, oldId uint64, newId uint64) error { diff --git a/components/engine/graphdriver/devmapper/devmapper_test.go b/components/engine/graphdriver/devmapper/devmapper_test.go index 8d93ab30b1..33167f0953 100644 --- a/components/engine/graphdriver/devmapper/devmapper_test.go +++ b/components/engine/graphdriver/devmapper/devmapper_test.go @@ -1,7 +1,6 @@ package devmapper import ( - "syscall" "testing" ) @@ -264,7 +263,7 @@ func dmAttachLoopDeviceFail(filename string, fd *int) string { return "" } -func sysGetBlockSizeFail(fd uintptr, size *uint64) syscall.Errno { +func sysGetBlockSizeFail(fd uintptr, size *uint64) sysErrno { return 1 } diff --git a/components/engine/graphdriver/devmapper/devmapper_wrapper.go b/components/engine/graphdriver/devmapper/devmapper_wrapper.go index e261db4351..00b7ecb383 100644 --- a/components/engine/graphdriver/devmapper/devmapper_wrapper.go +++ b/components/engine/graphdriver/devmapper/devmapper_wrapper.go @@ -140,7 +140,6 @@ static void log_with_errno_init() import "C" import ( - "syscall" "unsafe" ) @@ -239,23 +238,22 @@ func dmTaskAddTargetFct(task *CDmTask, C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) } -func dmGetLoopbackBackingFile(fd uintptr) (uint64, uint64, syscall.Errno) { +func dmGetLoopbackBackingFile(fd uintptr) (uint64, uint64, sysErrno) { var lo64 C.struct_loop_info64 - _, _, err := SyscallSyscall(syscall.SYS_IOCTL, fd, C.LOOP_GET_STATUS64, + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(&lo64))) - return uint64(lo64.lo_device), uint64(lo64.lo_inode), err + return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err) } -func dmLoopbackSetCapacity(fd uintptr) syscall.Errno { - _, _, err := SyscallSyscall(syscall.SYS_IOCTL, fd, C.LOOP_SET_CAPACITY, 0) - return err +func dmLoopbackSetCapacity(fd uintptr) sysErrno { + _, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0) + return sysErrno(err) } -func dmGetBlockSizeFct(fd uintptr) (int64, syscall.Errno) { +func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) { var size int64 - _, _, err := SyscallSyscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, - uintptr(unsafe.Pointer(&size))) - return size, err + _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) + return size, sysErrno(err) } func dmTaskGetInfoFct(task *CDmTask, info *Info) int { @@ -275,9 +273,7 @@ func dmTaskGetInfoFct(task *CDmTask, info *Info) int { return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) } -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 ( Cstart, Clength C.uint64_t CtargetType, Cparams *C.char @@ -288,6 +284,7 @@ func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, *target = C.GoString(CtargetType) *params = C.GoString(Cparams) }() + nextp := C.dm_get_next_target((*C.struct_dm_task)(task), unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) return uintptr(nextp) @@ -307,10 +304,9 @@ func dmAttachLoopDeviceFct(filename string, fd *int) string { return C.GoString(ret) } -func getBlockSizeFct(fd uintptr, size *uint64) syscall.Errno { - _, _, err := SyscallSyscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, - uintptr(unsafe.Pointer(&size))) - return err +func getBlockSizeFct(fd uintptr, size *uint64) sysErrno { + _, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))) + return sysErrno(err) } func dmUdevWaitFct(cookie uint) int { diff --git a/components/engine/graphdriver/devmapper/mount.go b/components/engine/graphdriver/devmapper/mount.go index e3a303e507..f066c51bbf 100644 --- a/components/engine/graphdriver/devmapper/mount.go +++ b/components/engine/graphdriver/devmapper/mount.go @@ -3,7 +3,6 @@ package devmapper import ( "os" "path/filepath" - "syscall" ) // FIXME: this is copy-pasted from the aufs driver. @@ -21,7 +20,7 @@ func Mounted(mountpoint string) (bool, error) { if err != nil { return false, err } - mntpointSt := mntpoint.Sys().(*syscall.Stat_t) - parentSt := parent.Sys().(*syscall.Stat_t) + mntpointSt := toSysStatT(mntpoint.Sys()) + parentSt := toSysStatT(parent.Sys()) return mntpointSt.Dev != parentSt.Dev, nil } diff --git a/components/engine/graphdriver/devmapper/sys.go b/components/engine/graphdriver/devmapper/sys.go index c2ba1263f0..d5fcde490a 100644 --- a/components/engine/graphdriver/devmapper/sys.go +++ b/components/engine/graphdriver/devmapper/sys.go @@ -1,15 +1,31 @@ package devmapper - import ( + "os" "syscall" ) +type ( + sysStatT syscall.Stat_t + sysErrno syscall.Errno +) var ( - SyscallMount = syscall.Mount - SyscallUnmount = syscall.Unmount - SyscallCloseOnExec = syscall.CloseOnExec - SyscallSyscall = syscall.Syscall - OSOpenFile = os.OpenFile + // functions + sysMount = syscall.Mount + sysUnmount = syscall.Unmount + sysCloseOnExec = syscall.CloseOnExec + sysSyscall = syscall.Syscall + osOpenFile = os.OpenFile ) + +const ( + sysMsMgcVal = syscall.MS_MGC_VAL + sysMsRdOnly = syscall.MS_RDONLY + sysEInval = syscall.EINVAL + sysSysIoctl = syscall.SYS_IOCTL +) + +func toSysStatT(i interface{}) *sysStatT { + return (*sysStatT)(i.(*syscall.Stat_t)) +} From 3bd981cb5359a3f041b57339c6de0f4c9f167641 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 20 Nov 2013 13:05:17 -0800 Subject: [PATCH 03/11] Remove os from devmapper Upstream-commit: a39bd656622c7dbea534fe34710ed6cb21dd5a90 Component: engine --- .../engine/graphdriver/devmapper/deviceset.go | 30 +++++++++---------- .../engine/graphdriver/devmapper/devmapper.go | 25 ++++++++-------- .../engine/graphdriver/devmapper/driver.go | 5 ++-- .../graphdriver/devmapper/driver_test.go | 11 ++++--- .../engine/graphdriver/devmapper/mount.go | 7 ++--- .../engine/graphdriver/devmapper/sys.go | 18 +++++++++-- 6 files changed, 52 insertions(+), 44 deletions(-) diff --git a/components/engine/graphdriver/devmapper/deviceset.go b/components/engine/graphdriver/devmapper/deviceset.go index ce66211cab..02d6e6bc3a 100644 --- a/components/engine/graphdriver/devmapper/deviceset.go +++ b/components/engine/graphdriver/devmapper/deviceset.go @@ -6,7 +6,6 @@ import ( "github.com/dotcloud/docker/utils" "io" "io/ioutil" - "os" "os/exec" "path" "path/filepath" @@ -104,7 +103,7 @@ func (devices *DeviceSet) hasImage(name string) bool { dirname := devices.loopbackDir() filename := path.Join(dirname, name) - _, err := os.Stat(filename) + _, err := osStat(filename) return err == nil } @@ -116,16 +115,16 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) { dirname := devices.loopbackDir() filename := path.Join(dirname, name) - if err := os.MkdirAll(dirname, 0700); err != nil && !os.IsExist(err) { + if err := osMkdirAll(dirname, 0700); err != nil && !osIsExist(err) { return "", err } - if _, err := os.Stat(filename); err != nil { - if !os.IsNotExist(err) { + if _, err := osStat(filename); err != nil { + if !osIsNotExist(err) { return "", err } utils.Debugf("Creating loopback file %s for device-manage use", filename) - file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600) + file, err := osOpenFile(filename, osORdWr|osOCreate, 0600) if err != nil { return "", err } @@ -173,7 +172,7 @@ func (devices *DeviceSet) saveMetadata() error { if err := tmpFile.Close(); err != nil { return fmt.Errorf("Error closing metadata file %s: %s", tmpFile.Name(), err) } - if err := os.Rename(tmpFile.Name(), devices.jsonFile()); err != nil { + if err := osRename(tmpFile.Name(), devices.jsonFile()); err != nil { return fmt.Errorf("Error committing metadata file", err) } @@ -251,7 +250,7 @@ func (devices *DeviceSet) loadMetaData() error { devices.NewTransactionId = devices.TransactionId jsonData, err := ioutil.ReadFile(devices.jsonFile()) - if err != nil && !os.IsNotExist(err) { + if err != nil && !osIsNotExist(err) { utils.Debugf("\n--->Err: %s\n", err) return err } @@ -336,10 +335,9 @@ func (devices *DeviceSet) setupBaseImage() error { } func setCloseOnExec(name string) { - fileInfos, _ := ioutil.ReadDir("/proc/self/fd") - if fileInfos != nil { + if fileInfos, _ := ioutil.ReadDir("/proc/self/fd"); fileInfos != nil { for _, i := range fileInfos { - link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name())) + link, _ := osReadlink(filepath.Join("/proc/self/fd", i.Name())) if link == name { fd, err := strconv.Atoi(i.Name()) if err == nil { @@ -371,7 +369,7 @@ func (devices *DeviceSet) ResizePool(size int64) error { datafilename := path.Join(dirname, "data") metadatafilename := path.Join(dirname, "metadata") - datafile, err := os.OpenFile(datafilename, os.O_RDWR, 0) + datafile, err := osOpenFile(datafilename, osORdWr, 0) if datafile == nil { return err } @@ -386,19 +384,19 @@ func (devices *DeviceSet) ResizePool(size int64) error { return fmt.Errorf("Can't shrink file") } - dataloopback := FindLoopDeviceFor(datafile) + dataloopback := FindLoopDeviceFor(&osFile{File: datafile}) if dataloopback == nil { return fmt.Errorf("Unable to find loopback mount for: %s", datafilename) } defer dataloopback.Close() - metadatafile, err := os.OpenFile(metadatafilename, os.O_RDWR, 0) + metadatafile, err := osOpenFile(metadatafilename, osORdWr, 0) if metadatafile == nil { return err } defer metadatafile.Close() - metadataloopback := FindLoopDeviceFor(metadatafile) + metadataloopback := FindLoopDeviceFor(&osFile{File: metadatafile}) if metadataloopback == nil { return fmt.Errorf("Unable to find loopback mount for: %s", metadatafilename) } @@ -463,7 +461,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { // Set the device prefix from the device id and inode of the docker root dir - st, err := os.Stat(devices.root) + st, err := osStat(devices.root) if err != nil { return fmt.Errorf("Error looking up dir %s: %s", devices.root, err) } diff --git a/components/engine/graphdriver/devmapper/devmapper.go b/components/engine/graphdriver/devmapper/devmapper.go index cccb168f95..103937b7ad 100644 --- a/components/engine/graphdriver/devmapper/devmapper.go +++ b/components/engine/graphdriver/devmapper/devmapper.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/utils" - "os" "runtime" ) @@ -179,16 +178,16 @@ func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, start, length, targetType, params } -func AttachLoopDevice(filename string) (*os.File, error) { +func AttachLoopDevice(filename string) (*osFile, error) { var fd int res := DmAttachLoopDevice(filename, &fd) if res == "" { return nil, ErrAttachLoopbackDevice } - return os.NewFile(uintptr(fd), res), nil + return &osFile{File: osNewFile(uintptr(fd), res)}, nil } -func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) { +func getLoopbackBackingFile(file *osFile) (uint64, uint64, error) { dev, inode, err := dmGetLoopbackBackingFile(file.Fd()) if err != 0 { return 0, 0, ErrGetLoopbackBackingFile @@ -196,7 +195,7 @@ func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) { return dev, inode, nil } -func LoopbackSetCapacity(file *os.File) error { +func LoopbackSetCapacity(file *osFile) error { err := dmLoopbackSetCapacity(file.Fd()) if err != 0 { return ErrLoopbackSetCapacity @@ -204,7 +203,7 @@ func LoopbackSetCapacity(file *os.File) error { return nil } -func FindLoopDeviceFor(file *os.File) *os.File { +func FindLoopDeviceFor(file *osFile) *osFile { stat, err := file.Stat() if err != nil { return nil @@ -215,9 +214,9 @@ func FindLoopDeviceFor(file *os.File) *os.File { for i := 0; true; i++ { path := fmt.Sprintf("/dev/loop%d", i) - file, err := osOpenFile(path, os.O_RDWR, 0) + file, err := osOpenFile(path, osORdWr, 0) if err != nil { - if os.IsNotExist(err) { + if osIsNotExist(err) { return nil } @@ -226,9 +225,9 @@ func FindLoopDeviceFor(file *os.File) *os.File { continue } - dev, inode, err := getLoopbackBackingFile(file) + dev, inode, err := getLoopbackBackingFile(&osFile{File: file}) if err == nil && dev == targetDevice && inode == targetInode { - return file + return &osFile{File: file} } file.Close() @@ -288,7 +287,7 @@ func RemoveDevice(name string) error { return nil } -func GetBlockDeviceSize(file *os.File) (uint64, error) { +func GetBlockDeviceSize(file *osFile) (uint64, error) { size, errno := DmGetBlockSize(file.Fd()) if size == -1 || errno != 0 { return 0, ErrGetBlockSize @@ -297,7 +296,7 @@ func GetBlockDeviceSize(file *os.File) (uint64, error) { } // This is the programmatic example of "dmsetup create" -func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error { +func createPool(poolName string, dataFile, metadataFile *osFile) error { task, err := createTask(DeviceCreate, poolName) if task == nil { return err @@ -327,7 +326,7 @@ func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error return nil } -func reloadPool(poolName string, dataFile *os.File, metadataFile *os.File) error { +func reloadPool(poolName string, dataFile, metadataFile *osFile) error { task, err := createTask(DeviceReload, poolName) if task == nil { return err diff --git a/components/engine/graphdriver/devmapper/driver.go b/components/engine/graphdriver/devmapper/driver.go index 55b8e3e720..70e62a0a80 100644 --- a/components/engine/graphdriver/devmapper/driver.go +++ b/components/engine/graphdriver/devmapper/driver.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/dotcloud/docker/graphdriver" "io/ioutil" - "os" "path" ) @@ -67,7 +66,7 @@ func (d *Driver) Create(id string, parent string) error { return err } - if err := os.MkdirAll(path.Join(mp, "rootfs"), 0755); err != nil && !os.IsExist(err) { + if err := osMkdirAll(path.Join(mp, "rootfs"), 0755); err != nil && !osIsExist(err) { return err } @@ -98,7 +97,7 @@ func (d *Driver) Get(id string) (string, error) { func (d *Driver) mount(id, mountPoint string) error { // Create the target directories if they don't exist - if err := os.MkdirAll(mountPoint, 0755); err != nil && !os.IsExist(err) { + if err := osMkdirAll(mountPoint, 0755); err != nil && !osIsExist(err) { return err } // If mountpoint is already mounted, do nothing diff --git a/components/engine/graphdriver/devmapper/driver_test.go b/components/engine/graphdriver/devmapper/driver_test.go index f8704950ad..02c614a270 100644 --- a/components/engine/graphdriver/devmapper/driver_test.go +++ b/components/engine/graphdriver/devmapper/driver_test.go @@ -2,7 +2,6 @@ package devmapper import ( "io/ioutil" - "os" "path" "testing" ) @@ -34,12 +33,12 @@ func newDriver(t *testing.T) *Driver { func cleanup(d *Driver) { d.Cleanup() - os.RemoveAll(d.home) + osRemoveAll(d.home) } func TestInit(t *testing.T) { home := mkTestDirectory(t) - defer os.RemoveAll(home) + defer osRemoveAll(home) driver, err := Init(home) if err != nil { t.Fatal(err) @@ -58,7 +57,7 @@ func TestInit(t *testing.T) { if err != nil { t.Fatal(err) } - if st, err := os.Stat(dir); err != nil { + if st, err := osStat(dir); err != nil { t.Fatal(err) } else if !st.IsDir() { t.Fatalf("Get(%V) did not return a directory", id) @@ -99,7 +98,7 @@ func TestDriverRemove(t *testing.T) { func TestCleanup(t *testing.T) { t.Skip("Unimplemented") d := newDriver(t) - defer os.RemoveAll(d.home) + defer osRemoveAll(d.home) mountPoints := make([]string, 2) @@ -284,7 +283,7 @@ func TestDriverGetSize(t *testing.T) { size := int64(1024) - f, err := os.Create(path.Join(mountPoint, "test_file")) + f, err := osCreate(path.Join(mountPoint, "test_file")) if err != nil { t.Fatal(err) } diff --git a/components/engine/graphdriver/devmapper/mount.go b/components/engine/graphdriver/devmapper/mount.go index f066c51bbf..3f75cfbcf9 100644 --- a/components/engine/graphdriver/devmapper/mount.go +++ b/components/engine/graphdriver/devmapper/mount.go @@ -1,7 +1,6 @@ package devmapper import ( - "os" "path/filepath" ) @@ -9,14 +8,14 @@ import ( // It should be moved into the core. func Mounted(mountpoint string) (bool, error) { - mntpoint, err := os.Stat(mountpoint) + mntpoint, err := osStat(mountpoint) if err != nil { - if os.IsNotExist(err) { + if osIsNotExist(err) { return false, nil } return false, err } - parent, err := os.Stat(filepath.Join(mountpoint, "..")) + parent, err := osStat(filepath.Join(mountpoint, "..")) if err != nil { return false, err } diff --git a/components/engine/graphdriver/devmapper/sys.go b/components/engine/graphdriver/devmapper/sys.go index d5fcde490a..8250e3ed7d 100644 --- a/components/engine/graphdriver/devmapper/sys.go +++ b/components/engine/graphdriver/devmapper/sys.go @@ -8,15 +8,26 @@ import ( type ( sysStatT syscall.Stat_t sysErrno syscall.Errno + + osFile struct{ *os.File } ) var ( - // functions sysMount = syscall.Mount sysUnmount = syscall.Unmount sysCloseOnExec = syscall.CloseOnExec sysSyscall = syscall.Syscall - osOpenFile = os.OpenFile + + osOpenFile = os.OpenFile + osNewFile = os.NewFile + osCreate = os.Create + osStat = os.Stat + osIsNotExist = os.IsNotExist + osIsExist = os.IsExist + osMkdirAll = os.MkdirAll + osRemoveAll = os.RemoveAll + osRename = os.Rename + osReadlink = os.Readlink ) const ( @@ -24,6 +35,9 @@ const ( sysMsRdOnly = syscall.MS_RDONLY sysEInval = syscall.EINVAL sysSysIoctl = syscall.SYS_IOCTL + + osORdWr = os.O_RDWR + osOCreate = os.O_CREATE ) func toSysStatT(i interface{}) *sysStatT { From 099a15779c5b39fcdf6586e83603a766cbeeafa1 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 20 Nov 2013 14:09:46 -0800 Subject: [PATCH 04/11] Add devmapper struct doc Upstream-commit: d233894c25e2b1b7124d69bddece94c88fb44452 Component: engine --- .../graphdriver/devmapper/devmapper_doc.go | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 components/engine/graphdriver/devmapper/devmapper_doc.go diff --git a/components/engine/graphdriver/devmapper/devmapper_doc.go b/components/engine/graphdriver/devmapper/devmapper_doc.go new file mode 100644 index 0000000000..c1c3e3891b --- /dev/null +++ b/components/engine/graphdriver/devmapper/devmapper_doc.go @@ -0,0 +1,106 @@ +package devmapper + +// Definition of struct dm_task and sub structures (from lvm2) +// +// struct dm_ioctl { +// /* +// * The version number is made up of three parts: +// * major - no backward or forward compatibility, +// * minor - only backwards compatible, +// * patch - both backwards and forwards compatible. +// * +// * All clients of the ioctl interface should fill in the +// * version number of the interface that they were +// * compiled with. +// * +// * All recognised ioctl commands (ie. those that don't +// * return -ENOTTY) fill out this field, even if the +// * command failed. +// */ +// uint32_t version[3]; /* in/out */ +// uint32_t data_size; /* total size of data passed in +// * including this struct */ + +// uint32_t data_start; /* offset to start of data +// * relative to start of this struct */ + +// uint32_t target_count; /* in/out */ +// int32_t open_count; /* out */ +// uint32_t flags; /* in/out */ + +// /* +// * event_nr holds either the event number (input and output) or the +// * udev cookie value (input only). +// * The DM_DEV_WAIT ioctl takes an event number as input. +// * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls +// * use the field as a cookie to return in the DM_COOKIE +// * variable with the uevents they issue. +// * For output, the ioctls return the event number, not the cookie. +// */ +// uint32_t event_nr; /* in/out */ +// uint32_t padding; + +// uint64_t dev; /* in/out */ + +// char name[DM_NAME_LEN]; /* device name */ +// char uuid[DM_UUID_LEN]; /* unique identifier for +// * the block device */ +// char data[7]; /* padding or data */ +// }; + +// struct target { +// uint64_t start; +// uint64_t length; +// char *type; +// char *params; + +// struct target *next; +// }; + +// typedef enum { +// DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */ +// DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */ +// } dm_add_node_t; + +// struct dm_task { +// int type; +// char *dev_name; +// char *mangled_dev_name; + +// struct target *head, *tail; + +// int read_only; +// uint32_t event_nr; +// int major; +// int minor; +// int allow_default_major_fallback; +// uid_t uid; +// gid_t gid; +// mode_t mode; +// uint32_t read_ahead; +// uint32_t read_ahead_flags; +// union { +// struct dm_ioctl *v4; +// } dmi; +// char *newname; +// char *message; +// char *geometry; +// uint64_t sector; +// int no_flush; +// int no_open_count; +// int skip_lockfs; +// int query_inactive_table; +// int suppress_identical_reload; +// dm_add_node_t add_node; +// uint64_t existing_table_size; +// int cookie_set; +// int new_uuid; +// int secure_data; +// int retry_remove; +// int enable_checks; +// int expected_errno; + +// char *uuid; +// char *mangled_uuid; +// }; +// From d1d0a44d0fe3f57034a427d4dd90e4df4be09967 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 20 Nov 2013 23:12:19 +0000 Subject: [PATCH 05/11] devmapper: fix typo Upstream-commit: 023ff36704ef5ad8d6fc8d0c07f718fec2ec5ff0 Component: engine --- components/engine/graphdriver/devmapper/devmapper.go | 2 +- components/engine/graphdriver/devmapper/devmapper_wrapper.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/graphdriver/devmapper/devmapper.go b/components/engine/graphdriver/devmapper/devmapper.go index 103937b7ad..4ba995754a 100644 --- a/components/engine/graphdriver/devmapper/devmapper.go +++ b/components/engine/graphdriver/devmapper/devmapper.go @@ -84,7 +84,7 @@ type ( func (t *Task) destroy() { if t != nil { - DmTaskDestory(t.unmanaged) + DmTaskDestroy(t.unmanaged) runtime.SetFinalizer(t, nil) } } diff --git a/components/engine/graphdriver/devmapper/devmapper_wrapper.go b/components/engine/graphdriver/devmapper/devmapper_wrapper.go index 00b7ecb383..51bf40967a 100644 --- a/components/engine/graphdriver/devmapper/devmapper_wrapper.go +++ b/components/engine/graphdriver/devmapper/devmapper_wrapper.go @@ -148,7 +148,7 @@ type ( ) var ( - DmTaskDestory = dmTaskDestroyFct + DmTaskDestroy = dmTaskDestroyFct DmTaskCreate = dmTaskCreateFct DmTaskRun = dmTaskRunFct DmTaskSetName = dmTaskSetNameFct From f3353c818bdd1f1e26e6e6222e3999f975838e7c Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 20 Nov 2013 23:25:27 +0000 Subject: [PATCH 06/11] Devmapper: remove deprecated test helpers Upstream-commit: da514223d1e441e2bab82bf3b30e1a1e0e222ac6 Component: engine --- components/engine/graphdriver/devmapper/devmapper.go | 1 - components/engine/graphdriver/devmapper/devmapper_test.go | 8 -------- 2 files changed, 9 deletions(-) diff --git a/components/engine/graphdriver/devmapper/devmapper.go b/components/engine/graphdriver/devmapper/devmapper.go index 4ba995754a..de82bde0fd 100644 --- a/components/engine/graphdriver/devmapper/devmapper.go +++ b/components/engine/graphdriver/devmapper/devmapper.go @@ -47,7 +47,6 @@ 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") diff --git a/components/engine/graphdriver/devmapper/devmapper_test.go b/components/engine/graphdriver/devmapper/devmapper_test.go index 33167f0953..8fd234cba2 100644 --- a/components/engine/graphdriver/devmapper/devmapper_test.go +++ b/components/engine/graphdriver/devmapper/devmapper_test.go @@ -246,10 +246,6 @@ func dmTaskAddTargetFail(task *CDmTask, return -1 } -func dmTaskGetDriverVersionFail(task *CDmTask, version *string) int { - return -1 -} - func dmTaskGetInfoFail(task *CDmTask, info *Info) int { return -1 } @@ -267,10 +263,6 @@ func sysGetBlockSizeFail(fd uintptr, size *uint64) sysErrno { return 1 } -func dmGetBlockSizeFail(fd uintptr) int64 { - return -1 -} - func dmUdevWaitFail(cookie uint) int { return -1 } From 91acb2bfd6bb13c17462dfad3dd141abfe547b29 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 20 Nov 2013 23:27:52 +0000 Subject: [PATCH 07/11] Devmapper: mock all calls to libdevmapper in the unit tests, and deny them by default Upstream-commit: e2390318bb4cc98886777bdad3fbb860942b8e65 Component: engine --- .../devmapper/devmapper_wrapper.go | 32 +++++----- .../graphdriver/devmapper/driver_test.go | 62 +++++++++++++++++++ 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/components/engine/graphdriver/devmapper/devmapper_wrapper.go b/components/engine/graphdriver/devmapper/devmapper_wrapper.go index 51bf40967a..1f28412197 100644 --- a/components/engine/graphdriver/devmapper/devmapper_wrapper.go +++ b/components/engine/graphdriver/devmapper/devmapper_wrapper.go @@ -148,26 +148,26 @@ type ( ) var ( - DmTaskDestroy = dmTaskDestroyFct - DmTaskCreate = dmTaskCreateFct - DmTaskRun = dmTaskRunFct - DmTaskSetName = dmTaskSetNameFct - DmTaskSetMessage = dmTaskSetMessageFct - DmTaskSetSector = dmTaskSetSectorFct - DmTaskSetCookie = dmTaskSetCookieFct - DmTaskSetAddNode = dmTaskSetAddNodeFct - DmTaskSetRo = dmTaskSetRoFct - DmTaskAddTarget = dmTaskAddTargetFct - DmTaskGetInfo = dmTaskGetInfoFct - DmGetNextTarget = dmGetNextTargetFct - DmGetBlockSize = dmGetBlockSizeFct DmAttachLoopDevice = dmAttachLoopDeviceFct - DmUdevWait = dmUdevWaitFct + DmGetBlockSize = dmGetBlockSizeFct + DmGetLibraryVersion = dmGetLibraryVersionFct + DmGetNextTarget = dmGetNextTargetFct DmLogInitVerbose = dmLogInitVerboseFct DmSetDevDir = dmSetDevDirFct - DmGetLibraryVersion = dmGetLibraryVersionFct - LogWithErrnoInit = logWithErrnoInitFct + DmTaskAddTarget = dmTaskAddTargetFct + DmTaskCreate = dmTaskCreateFct + DmTaskDestroy = dmTaskDestroyFct + DmTaskGetInfo = dmTaskGetInfoFct + DmTaskRun = dmTaskRunFct + DmTaskSetAddNode = dmTaskSetAddNodeFct + DmTaskSetCookie = dmTaskSetCookieFct + DmTaskSetMessage = dmTaskSetMessageFct + DmTaskSetName = dmTaskSetNameFct + DmTaskSetRo = dmTaskSetRoFct + DmTaskSetSector = dmTaskSetSectorFct + DmUdevWait = dmUdevWaitFct GetBlockSize = getBlockSizeFct + LogWithErrnoInit = logWithErrnoInitFct ) func free(p *C.char) { diff --git a/components/engine/graphdriver/devmapper/driver_test.go b/components/engine/graphdriver/devmapper/driver_test.go index 02c614a270..3e080c9004 100644 --- a/components/engine/graphdriver/devmapper/driver_test.go +++ b/components/engine/graphdriver/devmapper/driver_test.go @@ -12,6 +12,68 @@ func init() { DefaultMetaDataLoopbackSize = 200 * 1024 * 1024 DefaultBaseFsSize = 300 * 1024 * 1024 + // Hijack all calls to libdevmapper with default panics. + // Authorized calls are selectively hijacked in each tests. + DmTaskCreate = func(t int) *CDmTask { + panic("DmTaskCreate: this method should not be called here") + } + DmTaskRun = func(task *CDmTask) int { + panic("DmTaskRun: this method should not be called here") + } + DmTaskSetName = func(task *CDmTask, name string) int { + panic("DmTaskSetName: this method should not be called here") + } + DmTaskSetMessage = func(task *CDmTask, message string) int { + panic("DmTaskSetMessage: this method should not be called here") + } + DmTaskSetSector = func(task *CDmTask, sector uint64) int { + panic("DmTaskSetSector: this method should not be called here") + } + DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int { + panic("DmTaskSetCookie: this method should not be called here") + } + DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int { + panic("DmTaskSetAddNode: this method should not be called here") + } + DmTaskSetRo = func(task *CDmTask) int { + panic("DmTaskSetRo: this method should not be called here") + } + DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int { + panic("DmTaskAddTarget: this method should not be called here") + } + DmTaskGetInfo = func(task *CDmTask, info *Info) int { + panic("DmTaskGetInfo: this method should not be called here") + } + DmGetNextTarget = func(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr { + panic("DmGetNextTarget: this method should not be called here") + } + DmAttachLoopDevice = func(filename string, fd *int) string { + panic("DmAttachLoopDevice: this method should not be called here") + } + DmGetBlockSize = func(fd uintptr) (int64, sysErrno) { + panic("DmGetBlockSize: this method should not be called here") + } + DmUdevWait = func(cookie uint) int { + panic("DmUdevWait: this method should not be called here") + } + DmSetDevDir = func(dir string) int { + panic("DmSetDevDir: this method should not be called here") + } + DmGetLibraryVersion = func(version *string) int { + panic("DmGetLibraryVersion: this method should not be called here") + } + DmLogInitVerbose = func(level int) { + panic("DmLogInitVerbose: this method should not be called here") + } + DmTaskDestroy = func(task *CDmTask) { + panic("DmTaskDestroy: this method should not be called here") + } + GetBlockSize = func(fd uintptr, size *uint64) sysErrno { + panic("GetBlockSize: this method should not be called here") + } + LogWithErrnoInit = func() { + panic("LogWithErrnoInit: this method should not be called here") + } } func mkTestDirectory(t *testing.T) string { From 840cf279cf1c5f4ad62e9fcc87ff3dc1c4c17e50 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Thu, 21 Nov 2013 02:12:51 +0000 Subject: [PATCH 08/11] devmapper: skip test which are not unit tests Upstream-commit: 2b7c63b1b5d62515cd876f189dea6e5a5257cd49 Component: engine --- .../engine/graphdriver/devmapper/devmapper_test.go | 9 +++++++++ components/engine/graphdriver/devmapper/driver_test.go | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/components/engine/graphdriver/devmapper/devmapper_test.go b/components/engine/graphdriver/devmapper/devmapper_test.go index 8fd234cba2..ce22864361 100644 --- a/components/engine/graphdriver/devmapper/devmapper_test.go +++ b/components/engine/graphdriver/devmapper/devmapper_test.go @@ -5,6 +5,7 @@ import ( ) func TestTaskCreate(t *testing.T) { + t.Skip("FIXME: not a unit test") // Test success taskCreate(t, DeviceInfo) @@ -17,6 +18,7 @@ func TestTaskCreate(t *testing.T) { } func TestTaskRun(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -45,6 +47,7 @@ func TestTaskRun(t *testing.T) { } func TestTaskSetName(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -62,6 +65,7 @@ func TestTaskSetName(t *testing.T) { } func TestTaskSetMessage(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -79,6 +83,7 @@ func TestTaskSetMessage(t *testing.T) { } func TestTaskSetSector(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -96,6 +101,7 @@ func TestTaskSetSector(t *testing.T) { } func TestTaskSetCookie(t *testing.T) { + t.Skip("FIXME: not a unit test") var ( cookie uint = 0 task = taskCreate(t, DeviceInfo) @@ -120,6 +126,7 @@ func TestTaskSetCookie(t *testing.T) { } func TestTaskSetAddNode(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -141,6 +148,7 @@ func TestTaskSetAddNode(t *testing.T) { } func TestTaskSetRo(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success @@ -158,6 +166,7 @@ func TestTaskSetRo(t *testing.T) { } func TestTaskAddTarget(t *testing.T) { + t.Skip("FIXME: not a unit test") task := taskCreate(t, DeviceInfo) // Test success diff --git a/components/engine/graphdriver/devmapper/driver_test.go b/components/engine/graphdriver/devmapper/driver_test.go index 3e080c9004..e61bfa8981 100644 --- a/components/engine/graphdriver/devmapper/driver_test.go +++ b/components/engine/graphdriver/devmapper/driver_test.go @@ -127,6 +127,7 @@ func TestInit(t *testing.T) { } func TestDriverName(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) defer cleanup(d) @@ -136,6 +137,7 @@ func TestDriverName(t *testing.T) { } func TestDriverCreate(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) defer cleanup(d) @@ -145,6 +147,7 @@ func TestDriverCreate(t *testing.T) { } func TestDriverRemove(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) defer cleanup(d) @@ -158,6 +161,7 @@ func TestDriverRemove(t *testing.T) { } func TestCleanup(t *testing.T) { + t.Skip("FIXME: not a unit test") t.Skip("Unimplemented") d := newDriver(t) defer osRemoveAll(d.home) @@ -222,6 +226,7 @@ func TestCleanup(t *testing.T) { } func TestNotMounted(t *testing.T) { + t.Skip("FIXME: not a unit test") t.Skip("Not implemented") d := newDriver(t) defer cleanup(d) @@ -240,6 +245,7 @@ func TestNotMounted(t *testing.T) { } func TestMounted(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) defer cleanup(d) @@ -260,6 +266,7 @@ func TestMounted(t *testing.T) { } func TestInitCleanedDriver(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) if err := d.Create("1", ""); err != nil { @@ -286,6 +293,7 @@ func TestInitCleanedDriver(t *testing.T) { } func TestMountMountedDriver(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) defer cleanup(d) @@ -304,6 +312,7 @@ func TestMountMountedDriver(t *testing.T) { } func TestGetReturnsValidDevice(t *testing.T) { + t.Skip("FIXME: not a unit test") d := newDriver(t) defer cleanup(d) @@ -329,6 +338,7 @@ func TestGetReturnsValidDevice(t *testing.T) { } func TestDriverGetSize(t *testing.T) { + t.Skip("FIXME: not a unit test") t.Skipf("Size is currently not implemented") d := newDriver(t) From fe06edbbd147731d3fc7200a81e8de2cbd5220dc Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Thu, 21 Nov 2013 02:16:26 +0000 Subject: [PATCH 09/11] Devmapper: wrap calls to os/exec for easier mocking Upstream-commit: 60f728b170f550262b22b7905dfb280a405df4a7 Component: engine --- components/engine/graphdriver/devmapper/deviceset.go | 5 ++--- components/engine/graphdriver/devmapper/sys.go | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/engine/graphdriver/devmapper/deviceset.go b/components/engine/graphdriver/devmapper/deviceset.go index 02d6e6bc3a..d232b510d2 100644 --- a/components/engine/graphdriver/devmapper/deviceset.go +++ b/components/engine/graphdriver/devmapper/deviceset.go @@ -6,7 +6,6 @@ import ( "github.com/dotcloud/docker/utils" "io" "io/ioutil" - "os/exec" "path" "path/filepath" "strconv" @@ -223,9 +222,9 @@ func (devices *DeviceSet) activateDeviceIfNeeded(hash string) error { func (devices *DeviceSet) createFilesystem(info *DevInfo) error { devname := info.DevName() - err := exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname).Run() + err := execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname) if err != nil { - err = exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname).Run() + err = execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname) } if err != nil { utils.Debugf("\n--->Err: %s\n", err) diff --git a/components/engine/graphdriver/devmapper/sys.go b/components/engine/graphdriver/devmapper/sys.go index 8250e3ed7d..60bafb5f6d 100644 --- a/components/engine/graphdriver/devmapper/sys.go +++ b/components/engine/graphdriver/devmapper/sys.go @@ -2,6 +2,7 @@ package devmapper import ( "os" + "os/exec" "syscall" ) @@ -28,6 +29,10 @@ var ( osRemoveAll = os.RemoveAll osRename = os.Rename osReadlink = os.Readlink + + execRun = func(name string, args ...string) error { + return exec.Command(name, args...).Run() + } ) const ( From 50d7661c9b1ba13b25e589e957f0cc4b5517b884 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Thu, 21 Nov 2013 02:17:03 +0000 Subject: [PATCH 10/11] Devmapper: test driver initialization and its interaction with libdevmapper Upstream-commit: df258f5861794057c303a33c000bf830bc543631 Component: engine --- .../graphdriver/devmapper/driver_test.go | 244 ++++++++++++++++-- 1 file changed, 225 insertions(+), 19 deletions(-) diff --git a/components/engine/graphdriver/devmapper/driver_test.go b/components/engine/graphdriver/devmapper/driver_test.go index e61bfa8981..8b74d578c5 100644 --- a/components/engine/graphdriver/devmapper/driver_test.go +++ b/components/engine/graphdriver/devmapper/driver_test.go @@ -1,8 +1,11 @@ package devmapper import ( + "fmt" "io/ioutil" "path" + "runtime" + "strings" "testing" ) @@ -11,7 +14,10 @@ func init() { DefaultDataLoopbackSize = 300 * 1024 * 1024 DefaultMetaDataLoopbackSize = 200 * 1024 * 1024 DefaultBaseFsSize = 300 * 1024 * 1024 +} +// denyAllDevmapper mocks all calls to libdevmapper in the unit tests, and denies them by default +func denyAllDevmapper() { // Hijack all calls to libdevmapper with default panics. // Authorized calls are selectively hijacked in each tests. DmTaskCreate = func(t int) *CDmTask { @@ -98,32 +104,220 @@ func cleanup(d *Driver) { osRemoveAll(d.home) } +type Set map[string]bool + +func (r Set) Assert(t *testing.T, names ...string) { + for _, key := range names { + if _, exists := r[key]; !exists { + t.Fatalf("Key not set: %s", key) + } + delete(r, key) + } + if len(r) != 0 { + t.Fatalf("Unexpected keys: %v", r) + } +} + func TestInit(t *testing.T) { home := mkTestDirectory(t) defer osRemoveAll(home) - driver, err := Init(home) - if err != nil { - t.Fatal(err) - } - defer func() { - if err := driver.Cleanup(); err != nil { + calls := make(Set) + devicesAttached := make(Set) + taskMessages := make(Set) + taskTypes := make(Set) + func() { + denyAllDevmapper() + DmSetDevDir = func(dir string) int { + calls["DmSetDevDir"] = true + expectedDir := "/dev" + if dir != expectedDir { + t.Fatalf("Wrong libdevmapper call\nExpected: DmSetDevDir(%v)\nReceived: DmSetDevDir(%v)\n", expectedDir, dir) + } + return 0 + } + LogWithErrnoInit = func() { + calls["DmLogWithErrnoInit"] = true + } + var task1 CDmTask + DmTaskCreate = func(taskType int) *CDmTask { + calls["DmTaskCreate"] = true + taskTypes[fmt.Sprintf("%d", taskType)] = true + return &task1 + } + DmTaskSetName = func(task *CDmTask, name string) int { + calls["DmTaskSetName"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", expectedTask, task) + } + // FIXME: use Set.AssertRegexp() + if !strings.HasPrefix(name, "docker-") && !strings.HasPrefix(name, "/dev/mapper/docker-") || + !strings.HasSuffix(name, "-pool") && !strings.HasSuffix(name, "-base") { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", "docker-...-pool", name) + } + return 1 + } + DmTaskRun = func(task *CDmTask) int { + calls["DmTaskRun"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskRun(%v)\nReceived: DmTaskRun(%v)\n", expectedTask, task) + } + return 1 + } + DmTaskGetInfo = func(task *CDmTask, info *Info) int { + calls["DmTaskGetInfo"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskGetInfo(%v)\nReceived: DmTaskGetInfo(%v)\n", expectedTask, task) + } + // This will crash if info is not dereferenceable + info.Exists = 0 + return 1 + } + DmTaskSetSector = func(task *CDmTask, sector uint64) int { + calls["DmTaskSetSector"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task) + } + if expectedSector := uint64(0); sector != expectedSector { + t.Fatalf("Wrong libdevmapper call to DmTaskSetSector\nExpected: %v\nReceived: %v\n", expectedSector, sector) + } + return 1 + } + DmTaskSetMessage = func(task *CDmTask, message string) int { + calls["DmTaskSetMessage"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task) + } + taskMessages[message] = true + return 1 + } + var ( + fakeDataLoop = "/dev/loop42" + fakeMetadataLoop = "/dev/loop43" + fakeDataLoopFd = 42 + fakeMetadataLoopFd = 43 + ) + var attachCount int + DmAttachLoopDevice = func(filename string, fd *int) string { + calls["DmAttachLoopDevice"] = true + if _, exists := devicesAttached[filename]; exists { + t.Fatalf("Already attached %s", filename) + } + devicesAttached[filename] = true + // This will crash if fd is not dereferenceable + if attachCount == 0 { + attachCount++ + *fd = fakeDataLoopFd + return fakeDataLoop + } else { + *fd = fakeMetadataLoopFd + return fakeMetadataLoop + } + } + DmTaskDestroy = func(task *CDmTask) { + calls["DmTaskDestroy"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task) + } + } + fakeBlockSize := int64(4242 * 512) + DmGetBlockSize = func(fd uintptr) (int64, sysErrno) { + calls["DmGetBlockSize"] = true + if expectedFd := uintptr(42); fd != expectedFd { + t.Fatalf("Wrong libdevmapper call\nExpected: DmGetBlockSize(%v)\nReceived: DmGetBlockSize(%v)\n", expectedFd, fd) + } + return fakeBlockSize, 0 + } + DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int { + calls["DmTaskSetTarget"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task) + } + if start != 0 { + t.Fatalf("Wrong start: %d != %d", start, 0) + } + if ttype != "thin" && ttype != "thin-pool" { + t.Fatalf("Wrong ttype: %s", ttype) + } + // Quick smoke test + if params == "" { + t.Fatalf("Params should not be empty") + } + return 1 + } + fakeCookie := uint(4321) + DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int { + calls["DmTaskSetCookie"] = true + expectedTask := &task1 + if task != expectedTask { + t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task) + } + if flags != 0 { + t.Fatalf("Cookie flags should be 0 (not %x)", flags) + } + *cookie = fakeCookie + return 1 + } + DmUdevWait = func(cookie uint) int { + calls["DmUdevWait"] = true + if cookie != fakeCookie { + t.Fatalf("Wrong cookie: %d != %d", cookie, fakeCookie) + } + return 1 + } + DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int { + if addNode != AddNodeOnCreate { + t.Fatalf("Wrong AddNoteType: %v (expected %v)", addNode, AddNodeOnCreate) + } + calls["DmTaskSetAddNode"] = true + return 1 + } + execRun = func(name string, args ...string) error { + calls["execRun"] = true + if name != "mkfs.ext4" { + t.Fatalf("Expected %s to be executed, not %s", "mkfs.ext4", name) + } + return nil + } + driver, err := Init(home) + if err != nil { t.Fatal(err) } + defer func() { + if err := driver.Cleanup(); err != nil { + t.Fatal(err) + } + }() }() - id := "foo" - if err := driver.Create(id, ""); err != nil { - t.Fatal(err) - } - dir, err := driver.Get(id) - if err != nil { - t.Fatal(err) - } - if st, err := osStat(dir); err != nil { - t.Fatal(err) - } else if !st.IsDir() { - t.Fatalf("Get(%V) did not return a directory", id) - } + runtime.GC() + calls.Assert(t, + "DmSetDevDir", + "DmLogWithErrnoInit", + "DmTaskSetName", + "DmTaskRun", + "DmTaskGetInfo", + "DmAttachLoopDevice", + "DmTaskDestroy", + "execRun", + "DmTaskCreate", + "DmGetBlockSize", + "DmTaskSetTarget", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetSector", + "DmTaskSetMessage", + "DmTaskSetAddNode", + ) + devicesAttached.Assert(t, path.Join(home, "devicemapper", "data"), path.Join(home, "devicemapper", "metadata")) + taskTypes.Assert(t, "0", "6", "17") + taskMessages.Assert(t, "create_thin 0", "set_transaction_id 0 1") } func TestDriverName(t *testing.T) { @@ -372,3 +566,15 @@ func TestDriverGetSize(t *testing.T) { // t.Fatalf("Expected size %d got %d", size, diffSize) // } } + +func assertMap(t *testing.T, m map[string]bool, keys ...string) { + for _, key := range keys { + if _, exists := m[key]; !exists { + t.Fatalf("Key not set: %s", key) + } + delete(m, key) + } + if len(m) != 0 { + t.Fatalf("Unexpected keys: %v", m) + } +} From 26fbc2e02779ed8fc6adb48897fd364af8529a04 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 21 Nov 2013 16:32:16 -0800 Subject: [PATCH 11/11] Forbid syscalls in tests, add 2 new unit tests Upstream-commit: bc82940a575944e4686db203356a8a3fb3a75217 Component: engine --- .../engine/graphdriver/devmapper/driver.go | 4 +- .../graphdriver/devmapper/driver_test.go | 336 ++++++++++++++++-- .../engine/graphdriver/devmapper/mount.go | 2 +- 3 files changed, 317 insertions(+), 25 deletions(-) diff --git a/components/engine/graphdriver/devmapper/driver.go b/components/engine/graphdriver/devmapper/driver.go index 70e62a0a80..b08d5768ef 100644 --- a/components/engine/graphdriver/devmapper/driver.go +++ b/components/engine/graphdriver/devmapper/driver.go @@ -21,7 +21,7 @@ type Driver struct { home string } -func Init(home string) (graphdriver.Driver, error) { +var Init = func(home string) (graphdriver.Driver, error) { deviceSet, err := NewDeviceSet(home, true) if err != nil { return nil, err @@ -56,7 +56,7 @@ func (d *Driver) Cleanup() error { return d.DeviceSet.Shutdown() } -func (d *Driver) Create(id string, parent string) error { +func (d *Driver) Create(id, parent string) error { if err := d.DeviceSet.AddDevice(id, parent); err != nil { return err } diff --git a/components/engine/graphdriver/devmapper/driver_test.go b/components/engine/graphdriver/devmapper/driver_test.go index 8b74d578c5..3204575dd9 100644 --- a/components/engine/graphdriver/devmapper/driver_test.go +++ b/components/engine/graphdriver/devmapper/driver_test.go @@ -2,10 +2,12 @@ package devmapper import ( "fmt" + "github.com/dotcloud/docker/graphdriver" "io/ioutil" "path" "runtime" "strings" + "syscall" "testing" ) @@ -82,6 +84,39 @@ func denyAllDevmapper() { } } +func denyAllSyscall() { + sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) { + panic("sysMount: this method should not be called here") + } + sysUnmount = func(target string, flags int) (err error) { + panic("sysUnmount: this method should not be called here") + } + sysCloseOnExec = func(fd int) { + panic("sysCloseOnExec: this method should not be called here") + } + sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + panic("sysSyscall: this method should not be called here") + } + // Not a syscall, but forbidding it here anyway + Mounted = func(mnt string) (bool, error) { + panic("devmapper.Mounted: this method should not be called here") + } + // osOpenFile = os.OpenFile + // osNewFile = os.NewFile + // osCreate = os.Create + // osStat = os.Stat + // osIsNotExist = os.IsNotExist + // osIsExist = os.IsExist + // osMkdirAll = os.MkdirAll + // osRemoveAll = os.RemoveAll + // osRename = os.Rename + // osReadlink = os.Readlink + + // execRun = func(name string, args ...string) error { + // return exec.Command(name, args...).Run() + // } +} + func mkTestDirectory(t *testing.T) string { dir, err := ioutil.TempDir("", "docker-test-devmapper-") if err != nil { @@ -119,12 +154,15 @@ func (r Set) Assert(t *testing.T, names ...string) { } func TestInit(t *testing.T) { - home := mkTestDirectory(t) + var ( + calls = make(Set) + devicesAttached = make(Set) + taskMessages = make(Set) + taskTypes = make(Set) + home = mkTestDirectory(t) + ) defer osRemoveAll(home) - calls := make(Set) - devicesAttached := make(Set) - taskMessages := make(Set) - taskTypes := make(Set) + func() { denyAllDevmapper() DmSetDevDir = func(dir string) int { @@ -295,8 +333,12 @@ func TestInit(t *testing.T) { } }() }() + // Put all tests in a funciton to make sure the garbage collection will + // occur. + // Call GC to cleanup runtime.Finalizers runtime.GC() + calls.Assert(t, "DmSetDevDir", "DmLogWithErrnoInit", @@ -320,38 +362,288 @@ func TestInit(t *testing.T) { taskMessages.Assert(t, "create_thin 0", "set_transaction_id 0 1") } -func TestDriverName(t *testing.T) { - t.Skip("FIXME: not a unit test") - d := newDriver(t) - defer cleanup(d) +func fakeInit() func(home string) (graphdriver.Driver, error) { + oldInit := Init + Init = func(home string) (graphdriver.Driver, error) { + return &Driver{ + home: home, + }, nil + } + return oldInit +} +func restoreInit(init func(home string) (graphdriver.Driver, error)) { + Init = init +} + +func mockAllDevmapper(calls Set) { + DmSetDevDir = func(dir string) int { + calls["DmSetDevDir"] = true + return 0 + } + LogWithErrnoInit = func() { + calls["DmLogWithErrnoInit"] = true + } + DmTaskCreate = func(taskType int) *CDmTask { + calls["DmTaskCreate"] = true + return &CDmTask{} + } + DmTaskSetName = func(task *CDmTask, name string) int { + calls["DmTaskSetName"] = true + return 1 + } + DmTaskRun = func(task *CDmTask) int { + calls["DmTaskRun"] = true + return 1 + } + DmTaskGetInfo = func(task *CDmTask, info *Info) int { + calls["DmTaskGetInfo"] = true + return 1 + } + DmTaskSetSector = func(task *CDmTask, sector uint64) int { + calls["DmTaskSetSector"] = true + return 1 + } + DmTaskSetMessage = func(task *CDmTask, message string) int { + calls["DmTaskSetMessage"] = true + return 1 + } + DmAttachLoopDevice = func(filename string, fd *int) string { + calls["DmAttachLoopDevice"] = true + return "/dev/loop42" + } + DmTaskDestroy = func(task *CDmTask) { + calls["DmTaskDestroy"] = true + } + DmGetBlockSize = func(fd uintptr) (int64, sysErrno) { + calls["DmGetBlockSize"] = true + return int64(4242 * 512), 0 + } + DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int { + calls["DmTaskSetTarget"] = true + return 1 + } + DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int { + calls["DmTaskSetCookie"] = true + return 1 + } + DmUdevWait = func(cookie uint) int { + calls["DmUdevWait"] = true + return 1 + } + DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int { + calls["DmTaskSetAddNode"] = true + return 1 + } + execRun = func(name string, args ...string) error { + calls["execRun"] = true + return nil + } +} + +func TestDriverName(t *testing.T) { + denyAllDevmapper() + defer denyAllDevmapper() + + oldInit := fakeInit() + defer restoreInit(oldInit) + + d := newDriver(t) if d.String() != "devicemapper" { t.Fatalf("Expected driver name to be devicemapper got %s", d.String()) } } func TestDriverCreate(t *testing.T) { - t.Skip("FIXME: not a unit test") - d := newDriver(t) - defer cleanup(d) + denyAllDevmapper() + denyAllSyscall() + defer denyAllSyscall() + defer denyAllDevmapper() - if err := d.Create("1", ""); err != nil { - t.Fatal(err) + calls := make(Set) + mockAllDevmapper(calls) + + sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) { + calls["sysMount"] = true + // FIXME: compare the exact source and target strings (inodes + devname) + if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source) + } + if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target) + } + if expectedFstype := "ext4"; fstype != expectedFstype { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype) + } + if expectedFlags := uintptr(3236757504); flags != expectedFlags { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags) + } + return nil } + + Mounted = func(mnt string) (bool, error) { + calls["Mounted"] = true + if !strings.HasPrefix(mnt, "/tmp/docker-test-devmapper-") || !strings.HasSuffix(mnt, "/mnt/1") { + t.Fatalf("Wrong mounted call\nExpected: Mounted(%v)\nReceived: Mounted(%v)\n", "/tmp/docker-test-devmapper-.../mnt/1", mnt) + } + return false, nil + } + + func() { + d := newDriver(t) + + calls.Assert(t, + "DmSetDevDir", + "DmLogWithErrnoInit", + "DmTaskSetName", + "DmTaskRun", + "DmTaskGetInfo", + "DmAttachLoopDevice", + "execRun", + "DmTaskCreate", + "DmGetBlockSize", + "DmTaskSetTarget", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetSector", + "DmTaskSetMessage", + "DmTaskSetAddNode", + ) + + if err := d.Create("1", ""); err != nil { + t.Fatal(err) + } + calls.Assert(t, + "DmTaskCreate", + "DmTaskGetInfo", + "sysMount", + "Mounted", + "DmTaskRun", + "DmTaskSetTarget", + "DmTaskSetSector", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetName", + "DmTaskSetMessage", + "DmTaskSetAddNode", + ) + + }() + + runtime.GC() + + calls.Assert(t, + "DmTaskDestroy", + ) } func TestDriverRemove(t *testing.T) { - t.Skip("FIXME: not a unit test") - d := newDriver(t) - defer cleanup(d) + denyAllDevmapper() + denyAllSyscall() + defer denyAllSyscall() + defer denyAllDevmapper() - if err := d.Create("1", ""); err != nil { - t.Fatal(err) + calls := make(Set) + mockAllDevmapper(calls) + + sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) { + calls["sysMount"] = true + // FIXME: compare the exact source and target strings (inodes + devname) + if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source) + } + if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target) + } + if expectedFstype := "ext4"; fstype != expectedFstype { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype) + } + if expectedFlags := uintptr(3236757504); flags != expectedFlags { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags) + } + return nil + } + sysUnmount = func(target string, flags int) (err error) { + calls["sysUnmount"] = true + // FIXME: compare the exact source and target strings (inodes + devname) + if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target) + } + if expectedFlags := 0; flags != expectedFlags { + t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags) + } + return nil + } + Mounted = func(mnt string) (bool, error) { + calls["Mounted"] = true + return false, nil } - if err := d.Remove("1"); err != nil { - t.Fatal(err) - } + func() { + d := newDriver(t) + + calls.Assert(t, + "DmSetDevDir", + "DmLogWithErrnoInit", + "DmTaskSetName", + "DmTaskRun", + "DmTaskGetInfo", + "DmAttachLoopDevice", + "execRun", + "DmTaskCreate", + "DmGetBlockSize", + "DmTaskSetTarget", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetSector", + "DmTaskSetMessage", + "DmTaskSetAddNode", + ) + + if err := d.Create("1", ""); err != nil { + t.Fatal(err) + } + + calls.Assert(t, + "DmTaskCreate", + "DmTaskGetInfo", + "sysMount", + "Mounted", + "DmTaskRun", + "DmTaskSetTarget", + "DmTaskSetSector", + "DmTaskSetCookie", + "DmUdevWait", + "DmTaskSetName", + "DmTaskSetMessage", + "DmTaskSetAddNode", + ) + + Mounted = func(mnt string) (bool, error) { + calls["Mounted"] = true + return true, nil + } + + if err := d.Remove("1"); err != nil { + t.Fatal(err) + } + + calls.Assert(t, + "DmTaskRun", + "DmTaskSetSector", + "DmTaskSetName", + "DmTaskSetMessage", + "DmTaskCreate", + "DmTaskGetInfo", + "Mounted", + "sysUnmount", + ) + }() + runtime.GC() + + calls.Assert(t, + "DmTaskDestroy", + ) } func TestCleanup(t *testing.T) { diff --git a/components/engine/graphdriver/devmapper/mount.go b/components/engine/graphdriver/devmapper/mount.go index 3f75cfbcf9..7a07fff1e8 100644 --- a/components/engine/graphdriver/devmapper/mount.go +++ b/components/engine/graphdriver/devmapper/mount.go @@ -7,7 +7,7 @@ import ( // FIXME: this is copy-pasted from the aufs driver. // It should be moved into the core. -func Mounted(mountpoint string) (bool, error) { +var Mounted = func(mountpoint string) (bool, error) { mntpoint, err := osStat(mountpoint) if err != nil { if osIsNotExist(err) {