From c7a568eb933da415f1eed25c95f09957af009679 Mon Sep 17 00:00:00 2001 From: Rudy Zhang Date: Wed, 24 Oct 2018 22:26:10 +0800 Subject: [PATCH] refector: refect the pouch's volume module 1. don't return error when create volume is exist. 2. Refect the `VolumeID` to `VolumeContext`. 3. remove unuse field `Selectors` in `VolumeContext`. 4. set directory's quota id after check the limit of device. 5. get volume's size in `Extra` field. 6. add more options to set volume's size, you also can use "-o size=10g" or "-o Size=10g" or "-o opt.Size=10g" to set volume's size. 7. merge volume's error into `pkg/errtypes` 8. remove unuse package `pkg/serializer` Signed-off-by: Rudy Zhang --- daemon/mgr/container_storage.go | 163 ++++++++++++---------- daemon/mgr/volume.go | 32 ++--- pkg/errtypes/errors.go | 12 +- pkg/errtypes/volume_errors.go | 43 ++++++ pkg/serializer/interfaces.go | 19 --- pkg/serializer/serialize.go | 63 --------- pkg/utils/utils.go | 31 ++++ pkg/utils/utils_test.go | 56 ++++++++ storage/quota/grpquota.go | 16 +-- storage/quota/prjquota.go | 16 +-- storage/quota/quota.go | 61 +++++--- storage/volume/README.md | 12 +- storage/volume/core.go | 58 +++----- storage/volume/core_test.go | 34 ++--- storage/volume/driver/driver_interface.go | 2 +- storage/volume/driver/fake_driver.go | 4 +- storage/volume/driver/remote.go | 13 +- storage/volume/error/errors.go | 73 ---------- storage/volume/examples/demo/demo.go | 46 ------ storage/volume/modules/local/local.go | 26 ++-- storage/volume/modules/tmpfs/tmpfs.go | 33 ++++- storage/volume/types/meta/meta.go | 9 -- storage/volume/types/meta/types.go | 9 -- storage/volume/types/selector.go | 45 ------ storage/volume/types/volume.go | 81 ++++++----- storage/volume/types/volume_util.go | 29 +--- storage/volume/types/volume_util_test.go | 14 +- test/cli_volume_test.go | 13 +- 28 files changed, 460 insertions(+), 553 deletions(-) create mode 100644 pkg/errtypes/volume_errors.go delete mode 100644 pkg/serializer/interfaces.go delete mode 100644 pkg/serializer/serialize.go delete mode 100644 storage/volume/error/errors.go delete mode 100644 storage/volume/examples/demo/demo.go delete mode 100644 storage/volume/types/selector.go diff --git a/daemon/mgr/container_storage.go b/daemon/mgr/container_storage.go index bc7929f0c..74ed67f8d 100644 --- a/daemon/mgr/container_storage.go +++ b/daemon/mgr/container_storage.go @@ -32,7 +32,7 @@ func (mgr *ContainerManager) attachVolume(ctx context.Context, name string, c *C "backend": driver, } if _, err := mgr.VolumeMgr.Create(ctx, name, c.HostConfig.VolumeDriver, opts, nil); err != nil { - logrus.Errorf("failed to create volume(%s): %v", name, err) + logrus.Errorf("failed to create volume(%s), err(%v)", name, err) return "", "", errors.Wrap(err, "failed to create volume") } } else { @@ -40,13 +40,13 @@ func (mgr *ContainerManager) attachVolume(ctx context.Context, name string, c *C } if _, err := mgr.VolumeMgr.Attach(ctx, name, map[string]string{volumetypes.OptionRef: c.ID}); err != nil { - logrus.Errorf("failed to attach volume(%s):: %v", name, err) + logrus.Errorf("failed to attach volume(%s), err(%v)", name, err) return "", "", errors.Wrap(err, "failed to attach volume") } mountPath, err := mgr.VolumeMgr.Path(ctx, name) if err != nil { - logrus.Errorf("failed to get the mount path of volume(%s): %v", name, err) + logrus.Errorf("failed to get the mount path of volume(%s), err(%v)", name, err) return "", "", errors.Wrap(err, "failed to get volume mount path") } @@ -70,7 +70,7 @@ func (mgr *ContainerManager) generateMountPoints(ctx context.Context, c *Contain defer func() { if err != nil { if err := mgr.detachVolumes(ctx, c, false); err != nil { - logrus.Errorf("failed to detach volume: %v", err) + logrus.Errorf("failed to detach volume, err(%v)", err) } } }() @@ -124,7 +124,7 @@ func (mgr *ContainerManager) generateMountPoints(ctx context.Context, c *Contain func (mgr *ContainerManager) getMountPointFromBinds(ctx context.Context, c *Container, volumeSet map[string]struct{}) error { var err error - logrus.Debugf("bind volumes: %v", c.HostConfig.Binds) + logrus.Debugf("bind volumes(%v)", c.HostConfig.Binds) // parse binds for _, b := range c.HostConfig.Binds { @@ -151,11 +151,11 @@ func (mgr *ContainerManager) getMountPointFromBinds(ctx context.Context, c *Cont mode = parts[2] mp.Named = true default: - return errors.Errorf("unknown bind: %s", b) + return errors.Errorf("unknown bind(%s)", b) } if opts.CheckDuplicateMountPoint(c.Mounts, mp.Destination) { - logrus.Warnf("duplicate mount point: %s", mp.Destination) + logrus.Warnf("duplicate mountpoint(%s)", mp.Destination) continue } @@ -165,7 +165,7 @@ func (mgr *ContainerManager) getMountPointFromBinds(ctx context.Context, c *Cont err = opts.ParseBindMode(mp, mode) if err != nil { - logrus.Errorf("failed to parse bind mode(%s): %v", mode, err) + logrus.Errorf("failed to parse bind mode(%s), err(%v)", mode, err) return err } @@ -173,22 +173,29 @@ func (mgr *ContainerManager) getMountPointFromBinds(ctx context.Context, c *Cont // volume bind. name := mp.Source if _, exist := volumeSet[name]; !exist { - mp.Name = name - mp.Source, mp.Driver, err = mgr.attachVolume(ctx, name, c) + _, mp.Driver, err = mgr.attachVolume(ctx, name, c) if err != nil { - logrus.Errorf("failed to bind volume(%s): %v", name, err) + logrus.Errorf("failed to bind volume(%s), err(%v)", name, err) return errors.Wrap(err, "failed to bind volume") } - volumeSet[mp.Name] = struct{}{} + volumeSet[name] = struct{}{} } - if mp.Replace != "" { - mp.Source, err = mgr.VolumeMgr.Path(ctx, name) - if err != nil { - return err - } + volume, err := mgr.VolumeMgr.Get(ctx, name) + if err != nil || volume == nil { + logrus.Errorf("failed to get volume(%s), err(%v)", name, err) + return errors.Wrapf(err, "failed to get volume(%s)", name) + } + mp.Driver = volume.Driver() + mp.Name = name + + mp.Source, err = mgr.VolumeMgr.Path(ctx, name) + if err != nil { + return err + } + if mp.Replace != "" { switch mp.Replace { case "dr": mp.Source = path.Join(mp.Source, mp.Destination) @@ -202,17 +209,6 @@ func (mgr *ContainerManager) getMountPointFromBinds(ctx context.Context, c *Cont } } - if _, err = os.Stat(mp.Source); err != nil { - // host directory bind into container. - if !os.IsNotExist(err) { - return errors.Errorf("failed to stat %q: %v", mp.Source, err) - } - // Create the host path if it doesn't exist. - if err = os.MkdirAll(mp.Source, 0755); err != nil { - return errors.Errorf("failed to mkdir %q: %v", mp.Source, err) - } - } - c.Mounts = append(c.Mounts, mp) } @@ -225,7 +221,7 @@ func (mgr *ContainerManager) getMountPointFromVolumes(ctx context.Context, c *Co // parse volumes for dest := range c.Config.Volumes { if opts.CheckDuplicateMountPoint(c.Mounts, dest) { - logrus.Warnf("duplicate mount point: %s from volumes", dest) + logrus.Warnf("duplicate mountpoint(%s) from volumes", dest) continue } @@ -241,13 +237,13 @@ func (mgr *ContainerManager) getMountPointFromVolumes(ctx context.Context, c *Co mp.Source, mp.Driver, err = mgr.attachVolume(ctx, mp.Name, c) if err != nil { - logrus.Errorf("failed to bind volume(%s): %v", mp.Name, err) + logrus.Errorf("failed to bind volume(%s), err(%v)", mp.Name, err) return errors.Wrap(err, "failed to bind volume") } err = opts.ParseBindMode(mp, "") if err != nil { - logrus.Errorf("failed to parse mode: %v", err) + logrus.Errorf("failed to parse mode, err(%v)", err) return err } @@ -264,7 +260,7 @@ func (mgr *ContainerManager) getMountPointFromImage(ctx context.Context, c *Cont // parse volumes from image image, err := mgr.ImageMgr.GetImage(ctx, c.Image) if err != nil { - return errors.Wrapf(err, "failed to get image: %s", c.Image) + return errors.Wrapf(err, "failed to get image(%s)", c.Image) } for dest := range image.Config.Volumes { // check if volume has been created @@ -274,7 +270,7 @@ func (mgr *ContainerManager) getMountPointFromImage(ctx context.Context, c *Cont } if opts.CheckDuplicateMountPoint(c.Mounts, dest) { - logrus.Warnf("duplicate mount point: %s from image", dest) + logrus.Warnf("duplicate mountpoint(%s) from image", dest) continue } @@ -284,13 +280,13 @@ func (mgr *ContainerManager) getMountPointFromImage(ctx context.Context, c *Cont mp.Source, mp.Driver, err = mgr.attachVolume(ctx, mp.Name, c) if err != nil { - logrus.Errorf("failed to bind volume(%s): %v", mp.Name, err) + logrus.Errorf("failed to bind volume(%s), err(%v)", mp.Name, err) return errors.Wrap(err, "failed to bind volume") } err = opts.ParseBindMode(mp, "") if err != nil { - logrus.Errorf("failed to parse mode: %v", err) + logrus.Errorf("failed to parse mode, err(%v)", err) return err } @@ -337,7 +333,7 @@ func (mgr *ContainerManager) getMountPointFromContainers(ctx context.Context, co mp.Name = oldMountPoint.Name mp.Source, mp.Driver, err = mgr.attachVolume(ctx, oldMountPoint.Name, container) if err != nil { - logrus.Errorf("failed to bind volume(%s): %v", oldMountPoint.Name, err) + logrus.Errorf("failed to bind volume(%s), err(%v)", oldMountPoint.Name, err) return errors.Wrap(err, "failed to bind volume") } @@ -346,7 +342,7 @@ func (mgr *ContainerManager) getMountPointFromContainers(ctx context.Context, co err = opts.ParseBindMode(mp, mode) if err != nil { - logrus.Errorf("failed to parse volumes-from mode %s: %v", mode, err) + logrus.Errorf("failed to parse volumes-from mode(%s), err(%v)", mode, err) return err } @@ -361,24 +357,37 @@ func (mgr *ContainerManager) getMountPointFromContainers(ctx context.Context, co } func (mgr *ContainerManager) populateVolumes(ctx context.Context, c *Container) error { - for _, mnt := range c.Mounts { - if mnt.Driver == "tmpfs" { + for _, mp := range c.Mounts { + if mp.Driver == "tmpfs" { continue } - if !mnt.CopyData { + if !mp.CopyData { continue } - logrus.Debugf("copying image data from %s:%s, to %s, path: %s", - c.ID, mnt.Destination, mnt.Name, mnt.Source) + logrus.Debugf("copying image data from (%s:%s), to volume(%s) or path(%s)", + c.ID, mp.Destination, mp.Name, mp.Source) - imagePath := path.Join(c.MountFS, mnt.Destination) + imagePath := path.Join(c.MountFS, mp.Destination) - err := copyImageContent(imagePath, mnt.Source) + err := copyImageContent(imagePath, mp.Source) if err != nil { - logrus.Errorf("failed to populate volume[name: %s, source: %s]: %v", mnt.Name, mnt.Source, err) - return err + logrus.Errorf("failed to copy image contents, volume[imagepath(%s), source(%s)], err(%v)", imagePath, mp.Source, err) + return errors.Wrapf(err, "failed to copy image content, image(%s), host(%s)", imagePath, mp.Source) + } + } + + for _, mp := range c.Mounts { + if _, err := os.Stat(mp.Source); err != nil { + // host directory bind into container. + if !os.IsNotExist(err) { + return errors.Wrapf(err, "failed to stat %q", mp.Source) + } + // Create the host path if it doesn't exist. + if err = os.MkdirAll(mp.Source, 0755); err != nil { + return errors.Wrapf(err, "failed to mkdir %q", mp.Source) + } } } @@ -438,7 +447,7 @@ func (mgr *ContainerManager) setMountTab(ctx context.Context, c *Container) erro err := ioutil.WriteFile(mtabPath, []byte(context), 0644) if err != nil { - return errors.Wrapf(err, "failed to write file: (%s)", mtabPath) + return errors.Wrapf(err, "failed to write file(%s)", mtabPath) } logrus.Infof("write mtab file to (%s)", mtabPath) @@ -447,7 +456,7 @@ func (mgr *ContainerManager) setMountTab(ctx context.Context, c *Container) erro } func (mgr *ContainerManager) setRootfsQuota(ctx context.Context, c *Container) error { - logrus.Debugf("start to set rootfs quota, directory: (%s)", c.MountFS) + logrus.Debugf("start to set rootfs quota, dir(%s)", c.MountFS) if c.MountFS == "" { return nil @@ -465,13 +474,13 @@ func (mgr *ContainerManager) setRootfsQuota(ctx context.Context, c *Container) e id, err := strconv.Atoi(qid) if err != nil { - return errors.Wrapf(err, "failed to change quota id: (%s) from string to int", qid) + return errors.Wrapf(err, "failed to change quota id(%s) from string to int", qid) } // set rootfs quota _, err = quota.SetRootfsDiskQuota(c.MountFS, rootfsQuota, uint32(id)) if err != nil { - return errors.Wrapf(err, "failed to set rootfs quota, mountfs: (%s), quota: (%s), quota id: (%d)", + return errors.Wrapf(err, "failed to set rootfs quota, mountfs(%s), quota(%s), quota id(%d)", c.MountFS, rootfsQuota, id) } @@ -494,7 +503,7 @@ func (mgr *ContainerManager) setMountPointDiskQuota(ctx context.Context, c *Cont if c.Config.QuotaID != "" { id, err := strconv.Atoi(c.Config.QuotaID) if err != nil { - return errors.Wrapf(err, "invalid argument, QuotaID: %s", c.Config.QuotaID) + return errors.Wrapf(err, "invalid argument, QuotaID(%s)", c.Config.QuotaID) } // if QuotaID is < 0, it means pouchd alloc a unique quota id. @@ -539,19 +548,19 @@ func (mgr *ContainerManager) setMountPointDiskQuota(ctx context.Context, c *Cont if mp.Name != "" { v, err := mgr.VolumeMgr.Get(ctx, mp.Name) if err != nil { - logrus.Warnf("failed to get volume: %s", mp.Name) + logrus.Warnf("failed to get volume(%s)", mp.Name) continue } if v.Size() != "" { - logrus.Debugf("skip volume: %s with size", mp.Name) + logrus.Debugf("skip volume(%s) with size", mp.Name) continue } } // skip non-directory path. if fd, err := os.Stat(mp.Source); err != nil || !fd.IsDir() { - logrus.Debugf("skip non-directory path: %s", mp.Source) + logrus.Debugf("skip non-directory path(%s)", mp.Source) continue } @@ -576,7 +585,7 @@ func (mgr *ContainerManager) setMountPointDiskQuota(ctx context.Context, c *Cont err := quota.SetDiskQuota(mp.Source, size, qid) if err != nil { // just ignore set disk quota fail - logrus.Warnf("failed to set disk quota, directory: (%s), size: (%s), quota id: (%d), err: (%v)", + logrus.Warnf("failed to set disk quota, directory(%s), size(%s), quotaID(%d), err(%v)", mp.Source, size, qid, err) } } @@ -595,12 +604,12 @@ func (mgr *ContainerManager) detachVolumes(ctx context.Context, c *Container, re _, err := mgr.VolumeMgr.Detach(ctx, name, map[string]string{volumetypes.OptionRef: c.ID}) if err != nil { - logrus.Warnf("failed to detach volume: %s, err: %v", name, err) + logrus.Warnf("failed to detach volume(%s), err(%v)", name, err) } if remove && !mount.Named { if err := mgr.VolumeMgr.Remove(ctx, name); err != nil && !errtypes.IsInUse(err) { - logrus.Warnf("failed to remove volume: %s when remove container", name) + logrus.Warnf("failed to remove volume(%s) when remove container", name) } } } @@ -642,7 +651,7 @@ func (mgr *ContainerManager) Unmount(ctx context.Context, c *Container) error { // TODO: if umount is failed, and how to deal it. err := mount.Unmount(c.MountFS, 0) if err != nil { - return errors.Wrapf(err, "failed to umount mountfs: (%s)", c.MountFS) + return errors.Wrapf(err, "failed to umount mountfs(%s)", c.MountFS) } return os.RemoveAll(c.MountFS) @@ -650,15 +659,15 @@ func (mgr *ContainerManager) Unmount(ctx context.Context, c *Container) error { func (mgr *ContainerManager) initContainerStorage(ctx context.Context, c *Container) (err error) { if err = mgr.Mount(ctx, c); err != nil { - return errors.Wrapf(err, "failed to mount rootfs: (%s)", c.MountFS) + return errors.Wrapf(err, "failed to mount rootfs(%s)", c.MountFS) } defer func() { if umountErr := mgr.Unmount(ctx, c); umountErr != nil { if err != nil { - err = errors.Wrapf(err, "failed to umount rootfs: (%s), err: (%v)", c.MountFS, umountErr) + err = errors.Wrapf(err, "failed to umount rootfs(%s), err(%v)", c.MountFS, umountErr) } else { - err = errors.Wrapf(umountErr, "failed to umount rootfs: (%s)", c.MountFS) + err = errors.Wrapf(umountErr, "failed to umount rootfs(%s)", c.MountFS) } } }() @@ -680,7 +689,7 @@ func (mgr *ContainerManager) initContainerStorage(ctx context.Context, c *Contai // set rootfs disk quota if err = mgr.setRootfsQuota(ctx, c); err != nil { - logrus.Warnf("failed to set rootfs disk quota, err: %v", err) + logrus.Warnf("failed to set rootfs disk quota, err(%v)", err) } return nil @@ -719,27 +728,37 @@ func copyImageContent(source, destination string) error { return nil } + fi, err = os.Stat(destination) + if err != nil { + if !os.IsNotExist(err) { + return err + } + + // destination directory is not exist, so mkdir for it. + logrus.Warnf("(%s) is not exist", destination) + if err := os.MkdirAll(destination, 755); err != nil && !os.IsExist(err) { + return err + } + } else if !fi.IsDir() { + return nil + } + + return copyExistContents(source, destination) +} + +func copyExistContents(source, destination string) error { volList, err := ioutil.ReadDir(source) if err != nil { return err } if len(volList) > 0 { - fi, err := os.Stat(destination) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } else if !fi.IsDir() { - return nil - } - dstList, err := ioutil.ReadDir(destination) if err != nil { return err } if len(dstList) == 0 { + logrus.Debugf("copy (%s) to (%s) with tar", source, destination) err := archive.CopyWithTar(source, destination) if err != nil { logrus.Errorf("copyImageContent: %v", err) diff --git a/daemon/mgr/volume.go b/daemon/mgr/volume.go index b241a84ce..1c0be2609 100644 --- a/daemon/mgr/volume.go +++ b/daemon/mgr/volume.go @@ -6,6 +6,7 @@ import ( "github.com/alibaba/pouch/daemon/events" "github.com/alibaba/pouch/pkg/errtypes" + "github.com/alibaba/pouch/pkg/utils" "github.com/alibaba/pouch/storage/volume" "github.com/alibaba/pouch/storage/volume/types" @@ -65,12 +66,11 @@ func (vm *VolumeManager) Create(ctx context.Context, name, driver string, option driver = types.DefaultBackend } - id := types.VolumeID{ - Name: name, - Driver: driver, - Options: map[string]string{}, - Selectors: map[string]string{}, - Labels: map[string]string{}, + id := types.VolumeContext{ + Name: name, + Driver: driver, + Options: map[string]string{}, + Labels: map[string]string{}, } if labels != nil { @@ -83,6 +83,9 @@ func (vm *VolumeManager) Create(ctx context.Context, name, driver string, option v, err := vm.core.CreateVolume(id) if err != nil { + if errtypes.IsVolumeExisted(err) { + return v, nil + } return nil, err } @@ -93,7 +96,7 @@ func (vm *VolumeManager) Create(ctx context.Context, name, driver string, option // Get returns the information of volume that specified name/id. func (vm *VolumeManager) Get(ctx context.Context, name string) (*types.Volume, error) { - id := types.VolumeID{ + id := types.VolumeContext{ Name: name, } vol, err := vm.core.GetVolume(id) @@ -123,7 +126,7 @@ func (vm *VolumeManager) Remove(ctx context.Context, name string) error { return errors.Wrapf(errtypes.ErrVolumeInUse, "failed to remove volume(%s)", name) } - id := types.VolumeID{ + id := types.VolumeContext{ Name: name, } if err := vm.core.RemoveVolume(id); err != nil { @@ -140,7 +143,7 @@ func (vm *VolumeManager) Remove(ctx context.Context, name string) error { // Path returns the mount path of volume. func (vm *VolumeManager) Path(ctx context.Context, name string) (string, error) { - id := types.VolumeID{ + id := types.VolumeContext{ Name: name, } return vm.core.VolumePath(id) @@ -148,7 +151,7 @@ func (vm *VolumeManager) Path(ctx context.Context, name string) (string, error) // Attach is used to bind a volume to container. func (vm *VolumeManager) Attach(ctx context.Context, name string, options map[string]string) (*types.Volume, error) { - id := types.VolumeID{ + id := types.VolumeContext{ Name: name, } @@ -176,7 +179,7 @@ func (vm *VolumeManager) Attach(ctx context.Context, name string, options map[st // Detach is used to unbind a volume from container. func (vm *VolumeManager) Detach(ctx context.Context, name string, options map[string]string) (*types.Volume, error) { - id := types.VolumeID{ + id := types.VolumeContext{ Name: name, } @@ -198,12 +201,7 @@ func (vm *VolumeManager) Detach(ctx context.Context, name string, options map[st if ref != "" { ids := strings.Split(ref, ",") - for i, id := range ids { - if id == cid { - ids = append(ids[:i], ids[i+1:]...) - } - } - + ids = utils.StringSliceDelete(ids, cid) if len(ids) > 0 { options[types.OptionRef] = strings.Join(ids, ",") } else { diff --git a/pkg/errtypes/errors.go b/pkg/errtypes/errors.go index d025d4ac4..cb8510c5a 100644 --- a/pkg/errtypes/errors.go +++ b/pkg/errtypes/errors.go @@ -29,11 +29,8 @@ var ( // ErrNotImplemented represents that the function is not implemented. ErrNotImplemented = errorType{codeNotImplemented, "not implemented"} - // ErrVolumeInUse represents that volume in use. - ErrVolumeInUse = errorType{codeInUse, "volume is in use"} - - // ErrVolumeNotFound represents that no such volume. - ErrVolumeNotFound = errorType{codeNotFound, "no such volume"} + // ErrInUse represents that object is using. + ErrInUse = errorType{codeInUse, "in use"} // ErrNotModified represents that the resource is not modified ErrNotModified = errorType{codeNotModified, "not modified"} @@ -50,6 +47,11 @@ const ( codeNotImplemented codeInUse codeNotModified + + // volume error code + codeVolumeExisted + codeVolumeDriverNotFound + codeVolumeMetaNotFound ) type errorType struct { diff --git a/pkg/errtypes/volume_errors.go b/pkg/errtypes/volume_errors.go new file mode 100644 index 000000000..3260204c5 --- /dev/null +++ b/pkg/errtypes/volume_errors.go @@ -0,0 +1,43 @@ +package errtypes + +var ( + // ErrVolumeInUse represents that volume in use. + ErrVolumeInUse = errorType{codeInUse, "volume is in use"} + + // ErrVolumeNotFound represents that no such volume. + ErrVolumeNotFound = errorType{codeNotFound, "no such volume"} + + // ErrVolumeExisted represents error is "volume exist" + ErrVolumeExisted = errorType{codeVolumeExisted, "volume exist"} + + // ErrVolumeDriverNotFound represents error is "driver not found" + ErrVolumeDriverNotFound = errorType{codeVolumeDriverNotFound, "driver not found"} + + // ErrVolumeMetaNotFound represents error is "local meta not found" + ErrVolumeMetaNotFound = errorType{codeVolumeMetaNotFound, "local meta not found"} +) + +// IsVolumeInUse is used to check error is volume in use. +func IsVolumeInUse(err error) bool { + return checkError(err, codeInUse) +} + +// IsVolumeNotFound is used to check error is volumeNotFound or not. +func IsVolumeNotFound(err error) bool { + return checkError(err, codeNotFound) +} + +// IsVolumeExisted is used to check error is volumeExisted or not. +func IsVolumeExisted(err error) bool { + return checkError(err, codeVolumeExisted) +} + +// IsVolumeDriverNotFound is used to check error is driverNotFound or not. +func IsVolumeDriverNotFound(err error) bool { + return checkError(err, codeVolumeDriverNotFound) +} + +// IsVolumeMetaNotFound is used to check error is localMetaNotFound or not. +func IsVolumeMetaNotFound(err error) bool { + return checkError(err, codeVolumeMetaNotFound) +} diff --git a/pkg/serializer/interfaces.go b/pkg/serializer/interfaces.go deleted file mode 100644 index 3ae386764..000000000 --- a/pkg/serializer/interfaces.go +++ /dev/null @@ -1,19 +0,0 @@ -package serializer - -import ( - "io" -) - -// Serializer is an interface generalizes object serialize operation. -type Serializer interface { - Encode(obj Object) ([]byte, error) - Decode(data []byte, obj Object) error - EncodeToStream(w io.Writer, obj Object) error - DecodeFromStream(r io.Reader, obj Object) error -} - -// Object is an interface to define object. -type Object interface{} - -// Codec is an instance of Serializer. -var Codec = NewSerializer() diff --git a/pkg/serializer/serialize.go b/pkg/serializer/serialize.go deleted file mode 100644 index a2274c687..000000000 --- a/pkg/serializer/serialize.go +++ /dev/null @@ -1,63 +0,0 @@ -package serializer - -import ( - "encoding/json" - "io" -) - -// ContentType is a defined type. -type ContentType string - -const ( - // ContentTypeJSON is a json ContentType. - ContentTypeJSON ContentType = "application/json" -) - -// String return ContentType as string type. -func (c ContentType) String() string { - return string(c) -} - -// Serialization is the default implement of interface Serializer. -type Serialization struct { -} - -// NewSerializer returns Serializer instance. -func NewSerializer() Serializer { - return &Serialization{} -} - -func (s *Serialization) encode(obj Object) ([]byte, error) { - return json.Marshal(obj) -} - -func (s *Serialization) decode(data []byte, obj Object) error { - return json.Unmarshal(data, obj) - -} - -// Encode is used to encode obj. -func (s *Serialization) Encode(obj Object) ([]byte, error) { - return s.encode(obj) -} - -// Decode is used to decode obj from date. -func (s *Serialization) Decode(data []byte, obj Object) error { - return s.decode(data, obj) -} - -// EncodeToStream is used to encode obj to stream. -func (s *Serialization) EncodeToStream(w io.Writer, obj Object) error { - b, err := s.Encode(obj) - if err != nil { - return err - } - w.Write(b) - w.Write([]byte("\n")) - return nil -} - -// DecodeFromStream is used to decode obj from stream. -func (s *Serialization) DecodeFromStream(r io.Reader, obj Object) error { - return json.NewDecoder(r).Decode(obj) -} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 3305886e9..bfd6fd7d3 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -361,3 +361,34 @@ func StringDefault(s string, val string) string { } return val } + +// ToStringMap changes the map[string]interface{} to map[string]string, +// If the interface is not string, it will be ignore. +func ToStringMap(in map[string]interface{}) map[string]string { + if in == nil { + return nil + } + out := make(map[string]string, 0) + for k, v := range in { + if s, ok := v.(string); ok { + out[k] = s + } + } + return out +} + +// StringSliceDelete deletes the `del` string in string slice. +func StringSliceDelete(in []string, del string) []string { + if in == nil { + return nil + } + + out := make([]string, 0) + for _, value := range in { + if value != del { + out = append(out, value) + } + } + + return out +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 6cb7c7a52..96e86dd42 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -576,3 +576,59 @@ func TestMergeMap(t *testing.T) { } } } + +func TestToStringMap(t *testing.T) { + type Expect struct { + isNil bool + key string + value string + } + tests := []struct { + m1 map[string]interface{} + expect Expect + }{ + {nil, Expect{true, "", ""}}, + {map[string]interface{}{"a": "a", "b": "b"}, Expect{false, "a", "a"}}, + {map[string]interface{}{"a": "a", "b": "b"}, Expect{false, "b", "b"}}, + {map[string]interface{}{"a": map[string]string{"aa": "aa"}, "b": "b"}, Expect{false, "a", ""}}, + {map[string]interface{}{"a": map[string]string{"aa": "aa"}, "b": "b"}, Expect{false, "b", "b"}}, + } + + for _, test := range tests { + m2 := ToStringMap(test.m1) + if (test.expect.isNil && m2 != nil) || (!test.expect.isNil && m2 == nil) { + t.Fatalf("ToStringMap(%v) expected: %v, but got %v", test.m1, test.expect, m2) + } + if m2[test.expect.key] != test.expect.value { + t.Fatalf("ToStringMap(%v) expected: %v, but got %v", test.m1, test.expect.value, m2[test.expect.key]) + } + } +} + +func TestStringSliceDelete(t *testing.T) { + type Expect struct { + index int + value string + } + tests := []struct { + s1 []string + del string + expect *Expect + }{ + {nil, "", nil}, + {[]string{"a", "b", "a"}, "a", &Expect{0, "b"}}, + {[]string{"a", "b", "a"}, "b", &Expect{0, "a"}}, + {[]string{"a", "b", "a", "c"}, "a", &Expect{1, "c"}}, + } + + for _, test := range tests { + s2 := StringSliceDelete(test.s1, test.del) + if test.expect == nil && s2 != nil { + t.Fatalf("StringSliceDelete(%v) expected: nil, but got %v", test.s1, s2) + } + + if s2 != nil && s2[test.expect.index] != test.expect.value { + t.Fatalf("StringSliceDelete(%v) expected: %v, but got %v", test.s1, test.expect.value, s2[test.expect.index]) + } + } +} diff --git a/storage/quota/grpquota.go b/storage/quota/grpquota.go index e3af71506..a11a05480 100644 --- a/storage/quota/grpquota.go +++ b/storage/quota/grpquota.go @@ -229,14 +229,6 @@ func (quota *GrpQuotaDriver) SetDiskQuota(dir string, size string, quotaID uint3 return errors.Errorf("failed to find mountpoint, dir: (%s)", dir) } - id, err := quota.SetSubtree(dir, quotaID) - if err != nil { - return errors.Wrapf(err, "failed to set subtree, dir: (%s), quota id: (%d)", dir, quotaID) - } - if id == 0 { - return errors.Errorf("failed to find quota id to set subtree") - } - // transfer limit from kbyte to byte limit, err := bytefmt.ToKilobytes(size) if err != nil { @@ -247,6 +239,14 @@ func (quota *GrpQuotaDriver) SetDiskQuota(dir string, size string, quotaID uint3 return err } + id, err := quota.SetSubtree(dir, quotaID) + if err != nil { + return errors.Wrapf(err, "failed to set subtree, dir: (%s), quota id: (%d)", dir, quotaID) + } + if id == 0 { + return errors.Errorf("failed to find quota id to set subtree") + } + return quota.setQuota(id, limit, mountPoint) } diff --git a/storage/quota/prjquota.go b/storage/quota/prjquota.go index 13dbb5095..c1899a5f9 100644 --- a/storage/quota/prjquota.go +++ b/storage/quota/prjquota.go @@ -132,14 +132,6 @@ func (quota *PrjQuotaDriver) SetDiskQuota(dir string, size string, quotaID uint3 return errors.Errorf("failed to find mountpoint, dir: (%s)", dir) } - id, err := quota.SetSubtree(dir, quotaID) - if err != nil { - return errors.Wrapf(err, "failed to set subtree, dir: (%s), quota id: (%d)", dir, quotaID) - } - if id == 0 { - return errors.Errorf("failed to find quota id to set subtree") - } - // transfer limit from kbyte to byte limit, err := bytefmt.ToKilobytes(size) if err != nil { @@ -150,6 +142,14 @@ func (quota *PrjQuotaDriver) SetDiskQuota(dir string, size string, quotaID uint3 return errors.Wrapf(err, "failed to check device limit, dir: (%s), limit: (%d)kb", dir, limit) } + id, err := quota.SetSubtree(dir, quotaID) + if err != nil { + return errors.Wrapf(err, "failed to set subtree, dir: (%s), quota id: (%d)", dir, quotaID) + } + if id == 0 { + return errors.Errorf("failed to find quota id to set subtree") + } + return quota.setQuota(id, limit, mountPoint) } diff --git a/storage/quota/quota.go b/storage/quota/quota.go index 865c9fce4..5c316e4d3 100644 --- a/storage/quota/quota.go +++ b/storage/quota/quota.go @@ -131,7 +131,7 @@ func SetSubtree(dir string, qid uint32) (uint32, error) { // SetDiskQuota is used to set quota for directory. func SetDiskQuota(dir string, size string, quotaID uint32) error { - logrus.Infof("set disk quota, dir: (%s), size: (%s), quotaID: (%d)", dir, size, quotaID) + logrus.Infof("set disk quota, dir(%s), size(%s), quotaID(%d)", dir, size, quotaID) if isRegular, err := CheckRegularFile(dir); err != nil || !isRegular { logrus.Debugf("set quota skip not regular file: %s", dir) return err @@ -174,6 +174,21 @@ func GetNextQuotaID() (uint32, error) { return GQuotaDriver.GetNextQuotaID() } +// GetQuotaID returns the quota id of directory, +// if no quota id, it will alloc the next available quota id. +func GetQuotaID(dir string) (uint32, error) { + id := GetQuotaIDInFileAttr(dir) + if id > 0 { + return id, nil + } + id, err := GetNextQuotaID() + if err != nil { + return 0, errors.Wrapf(err, "failed to get file(%s) quota id", dir) + } + + return id, nil +} + //GetDefaultQuota returns the default quota size. func GetDefaultQuota(quotas map[string]string) string { if quotas == nil { @@ -199,22 +214,24 @@ func GetDefaultQuota(quotas map[string]string) string { func SetRootfsDiskQuota(basefs, size string, quotaID uint32) (uint32, error) { overlayMountInfo, err := getOverlayMountInfo(basefs) if err != nil { - return 0, fmt.Errorf("failed to get overlay mount info: %v", err) + return 0, errors.Wrapf(err, "failed to get overlay(%s) mount info", basefs) } for _, dir := range []string{overlayMountInfo.Upper, overlayMountInfo.Work} { _, err = StartQuotaDriver(dir) if err != nil { - return 0, fmt.Errorf("failed to start quota driver: %v", err) + return 0, errors.Wrapf(err, "failed to start dir(%s) quota driver", dir) } - quotaID, err = SetSubtree(dir, quotaID) - if err != nil { - return 0, fmt.Errorf("failed to set subtree: %v", err) + if quotaID == 0 { + quotaID, err = GetQuotaID(dir) + if err != nil { + return 0, errors.Wrapf(err, "failed to get dir(%s) quota id", dir) + } } if err := SetDiskQuota(dir, size, quotaID); err != nil { - return 0, fmt.Errorf("failed to set disk quota: %v", err) + return 0, errors.Wrapf(err, "failed to set dir(%s) disk quota", dir) } } @@ -256,7 +273,7 @@ func CheckRegularFile(file string) (bool, error) { func getOverlayMountInfo(basefs string) (*OverlayMount, error) { output, err := ioutil.ReadFile(procMountFile) if err != nil { - logrus.Warnf("failed to ReadFile %s: %v", procMountFile, err) + logrus.Warnf("failed to read file(%s), err(%v)", procMountFile, err) return nil, err } @@ -357,7 +374,7 @@ func loadQuotaIDs(repquotaOpt string) (map[uint32]struct{}, uint32, error) { } } } - logrus.Infof("Load repquota ids: %d, list: %v", len(quotaIDs), quotaIDs) + logrus.Infof("Load repquota ids(%d), list(%v)", len(quotaIDs), quotaIDs) return quotaIDs, minID, nil } @@ -368,15 +385,15 @@ func getMountpoint(dir string) (string, error) { output, err := ioutil.ReadFile(procMountFile) if err != nil { - logrus.Warnf("failed to read file: (%s), err: (%v)", procMountFile, err) - return "", errors.Wrapf(err, "failed to read file: (%s)", procMountFile) + logrus.Warnf("failed to read file(%s), err(%v)", procMountFile, err) + return "", errors.Wrapf(err, "failed to read file(%s)", procMountFile) } devID, err := system.GetDevID(dir) if err != nil { - return "", errors.Wrapf(err, "failed to get device id for dir: (%s)", dir) + return "", errors.Wrapf(err, "failed to get device id for dir(%s)", dir) } - logrus.Debugf("get directory(%s) device id: (%d)", dir, devID) + logrus.Debugf("get dir(%s) device id(%d)", dir, devID) // /dev/sdb1 /home/pouch ext4 rw,relatime,prjquota,data=ordered 0 0 for _, line := range strings.Split(string(output), "\n") { @@ -406,10 +423,10 @@ func getMountpoint(dir string) (string, error) { } if mountPoint == "" { - return "", errors.Errorf("failed to get mount point of directory: (%s)", dir) + return "", errors.Errorf("failed to get mount point of dir(%s)", dir) } - logrus.Debugf("get the directory: (%s) mountpoint: (%s)", dir, mountPoint) + logrus.Debugf("get the dir(%s)'s mountpoint(%s)", dir, mountPoint) return mountPoint, nil } @@ -425,20 +442,20 @@ func setDevLimit(dir string, devID uint64) (uint64, error) { mp, err := getMountpoint(dir) if err != nil { - return 0, errors.Wrapf(err, "failed to set device limit, dir: (%s), devID: (%d)", dir, devID) + return 0, errors.Wrapf(err, "failed to set device limit, dir(%s), devID(%d)", dir, devID) } newDevID, _ := system.GetDevID(mp) if newDevID != devID { - return 0, errors.Errorf("failed to set device limit, no such device id: (%d), checked id: (%d)", + return 0, errors.Errorf("failed to set device limit, no such device id(%d), checked id(%d)", devID, newDevID) } // get storage upper limit of the device which the dir is on. var stfs syscall.Statfs_t if err := syscall.Statfs(mp, &stfs); err != nil { - logrus.Errorf("failed to get path: (%s) limit, err: (%v)", mp, err) - return 0, errors.Wrapf(err, "failed to get path: (%s) limit", mp) + logrus.Errorf("failed to get path(%s) limit, err(%v)", mp, err) + return 0, errors.Wrapf(err, "failed to get path(%s) limit", mp) } limit = stfs.Blocks * uint64(stfs.Bsize) @@ -446,7 +463,7 @@ func setDevLimit(dir string, devID uint64) (uint64, error) { devLimits[devID] = limit lock.Unlock() - logrus.Debugf("SetDevLimit: dir: (%s), mountpoint: (%s), limit: (%v) B", dir, mp, limit) + logrus.Debugf("SetDevLimit: dir(%s), mountpoint(%s), limit(%v) B", dir, mp, limit) return limit, nil } @@ -454,7 +471,7 @@ func setDevLimit(dir string, devID uint64) (uint64, error) { func checkDevLimit(dir string, size uint64) error { devID, err := system.GetDevID(dir) if err != nil { - return errors.Wrapf(err, "failed to get device id, dir: (%s)", dir) + return errors.Wrapf(err, "failed to get device id, dir(%s)", dir) } lock.Lock() @@ -463,7 +480,7 @@ func checkDevLimit(dir string, size uint64) error { if !exist { // if has not recorded, just add (dir, device, limit) to driver. if limit, err = setDevLimit(dir, devID); err != nil { - return errors.Wrapf(err, "failed to set device limit, dir: (%s), devID: (%d)", dir, devID) + return errors.Wrapf(err, "failed to set device limit, dir(%s), devID: (%d)", dir, devID) } } diff --git a/storage/volume/README.md b/storage/volume/README.md index 4b4542ff4..6b4cf4f5b 100644 --- a/storage/volume/README.md +++ b/storage/volume/README.md @@ -52,26 +52,26 @@ Core provides these functions is as following: ```go // GetVolume return a volume's info with specified name, If not errors. -func (c *Core) GetVolume(id types.VolumeID) (*types.Volume, error) +func (c *Core) GetVolume(id types.VolumeContext) (*types.Volume, error) // CreateVolume use to create a volume, if failed, will return error info. -func (c *Core) CreateVolume(id types.VolumeID) error +func (c *Core) CreateVolume(id types.VolumeContext) error // ListVolumeName return the name of all volumes only. // Param 'labels' use to filter the volume's names, only return those you want. func (c *Core) ListVolumeName(labels map[string]string) ([]string, error) // RemoveVolume remove volume from storage and meta information, if not success return error. -func (c *Core) RemoveVolume(id types.VolumeID) error +func (c *Core) RemoveVolume(id types.VolumeContext) error // VolumePath return the path of volume on node host. -func (c *Core) VolumePath(id types.VolumeID) (string, error) +func (c *Core) VolumePath(id types.VolumeContext) (string, error) // AttachVolume to enable a volume on local host. -func (c *Core) AttachVolume(id types.VolumeID, extra map[string]string) (*types.Volume, error) +func (c *Core) AttachVolume(id types.VolumeContext, extra map[string]string) (*types.Volume, error) // DetachVolume to disable a volume on local host. -func (c *Core) DetachVolume(id types.VolumeID, extra map[string]string) (*types.Volume, error) +func (c *Core) DetachVolume(id types.VolumeContext, extra map[string]string) (*types.Volume, error) ``` ### Driver diff --git a/storage/volume/core.go b/storage/volume/core.go index ede2db9ce..ef4b1d784 100644 --- a/storage/volume/core.go +++ b/storage/volume/core.go @@ -6,10 +6,10 @@ import ( "reflect" "strings" + "github.com/alibaba/pouch/pkg/errtypes" "github.com/alibaba/pouch/pkg/kmutex" metastore "github.com/alibaba/pouch/pkg/meta" "github.com/alibaba/pouch/storage/volume/driver" - volerr "github.com/alibaba/pouch/storage/volume/error" "github.com/alibaba/pouch/storage/volume/types" "github.com/pkg/errors" @@ -81,7 +81,7 @@ func NewCore(cfg Config) (*Core, error) { } // getVolume return a volume's info with specified name, If not errors. -func (c *Core) getVolume(id types.VolumeID) (*types.Volume, error) { +func (c *Core) getVolume(id types.VolumeContext) (*types.Volume, error) { ctx := driver.Contexts() // first, try to get volume from local store. @@ -89,7 +89,7 @@ func (c *Core) getVolume(id types.VolumeID) (*types.Volume, error) { if err == nil { v, ok := obj.(*types.Volume) if !ok { - return nil, volerr.ErrVolumeNotFound + return nil, errtypes.ErrVolumeNotFound } // get the volume driver. @@ -102,10 +102,10 @@ func (c *Core) getVolume(id types.VolumeID) (*types.Volume, error) { if d, ok := dv.(driver.Getter); ok { curV, err := d.Get(ctx, id.Name) if err != nil { - return nil, volerr.ErrVolumeNotFound + return nil, errtypes.ErrVolumeNotFound } - v.Status.MountPoint = curV.Status.MountPoint + return curV, nil } return v, nil @@ -142,11 +142,11 @@ func (c *Core) getVolume(id types.VolumeID) (*types.Volume, error) { return v, nil } - return nil, volerr.ErrVolumeNotFound + return nil, errtypes.ErrVolumeNotFound } // getVolumeDriver return the backend driver and volume with specified volume's id. -func (c *Core) getVolumeDriver(id types.VolumeID) (*types.Volume, driver.Driver, error) { +func (c *Core) getVolumeDriver(id types.VolumeContext) (*types.Volume, driver.Driver, error) { v, err := c.getVolume(id) if err != nil { return nil, nil, err @@ -158,20 +158,8 @@ func (c *Core) getVolumeDriver(id types.VolumeID) (*types.Volume, driver.Driver, return v, dv, nil } -// existVolume return 'true' if volume be found and not errors. -func (c *Core) existVolume(id types.VolumeID) (bool, error) { - _, err := c.getVolume(id) - if err != nil { - if ec, ok := err.(volerr.CoreError); ok && ec.IsVolumeNotFound() { - return false, nil - } - return false, err - } - return true, nil -} - // GetVolume return a volume's info with specified name, If not errors. -func (c *Core) GetVolume(id types.VolumeID) (*types.Volume, error) { +func (c *Core) GetVolume(id types.VolumeContext) (*types.Volume, error) { c.lock.Lock(id.Name) defer c.lock.Unlock(id.Name) @@ -179,15 +167,16 @@ func (c *Core) GetVolume(id types.VolumeID) (*types.Volume, error) { } // CreateVolume use to create a volume, if failed, will return error info. -func (c *Core) CreateVolume(id types.VolumeID) (*types.Volume, error) { +func (c *Core) CreateVolume(id types.VolumeContext) (*types.Volume, error) { c.lock.Lock(id.Name) defer c.lock.Unlock(id.Name) - exist, err := c.existVolume(id) - if err != nil { + volume, err := c.getVolume(id) + if err == nil { + return volume, errtypes.ErrVolumeExisted + } + if !(errtypes.IsVolumeNotFound(err)) { return nil, err - } else if exist { - return nil, volerr.ErrVolumeExisted } dv, err := driver.Get(id.Driver) @@ -195,7 +184,7 @@ func (c *Core) CreateVolume(id types.VolumeID) (*types.Volume, error) { return nil, err } - volume, err := dv.Create(driver.Contexts(), id) + volume, err = dv.Create(driver.Contexts(), id) if err != nil { return nil, err } @@ -229,11 +218,8 @@ func (c *Core) ListVolumes(labels map[string]string) ([]*types.Volume, error) { ctx := driver.Contexts() var realVolumes = map[string]*types.Volume{} - var volumeDrivers = map[string]driver.Driver{} for _, dv := range drivers { - volumeDrivers[dv.Name(ctx)] = dv - d, ok := dv.(driver.Lister) if !ok { // not Lister, ignore it. @@ -256,13 +242,11 @@ func (c *Core) ListVolumes(labels map[string]string) ([]*types.Volume, error) { continue } - d, ok := volumeDrivers[v.Spec.Backend] - if !ok { - // driver not exist, ignore it + d, err := driver.Get(v.Spec.Backend) + if err != nil || d == nil { continue } - // the local driver and tmpfs driver if d.StoreMode(ctx).IsLocal() { retVolumes = append(retVolumes, v) continue @@ -323,7 +307,7 @@ func (c *Core) ListVolumeName(labels map[string]string) ([]string, error) { } // RemoveVolume remove volume from storage and meta information, if not success return error. -func (c *Core) RemoveVolume(id types.VolumeID) error { +func (c *Core) RemoveVolume(id types.VolumeContext) error { c.lock.Lock(id.Name) defer c.lock.Unlock(id.Name) @@ -341,7 +325,7 @@ func (c *Core) RemoveVolume(id types.VolumeID) error { } // VolumePath return the path of volume on node host. -func (c *Core) VolumePath(id types.VolumeID) (string, error) { +func (c *Core) VolumePath(id types.VolumeContext) (string, error) { c.lock.Lock(id.Name) defer c.lock.Unlock(id.Name) @@ -354,7 +338,7 @@ func (c *Core) VolumePath(id types.VolumeID) (string, error) { } // AttachVolume to enable a volume on local host. -func (c *Core) AttachVolume(id types.VolumeID, extra map[string]string) (*types.Volume, error) { +func (c *Core) AttachVolume(id types.VolumeContext, extra map[string]string) (*types.Volume, error) { c.lock.Lock(id.Name) defer c.lock.Unlock(id.Name) @@ -385,7 +369,7 @@ func (c *Core) AttachVolume(id types.VolumeID, extra map[string]string) (*types. } // DetachVolume to disable a volume on local host. -func (c *Core) DetachVolume(id types.VolumeID, extra map[string]string) (*types.Volume, error) { +func (c *Core) DetachVolume(id types.VolumeContext, extra map[string]string) (*types.Volume, error) { c.lock.Lock(id.Name) defer c.lock.Unlock(id.Name) diff --git a/storage/volume/core_test.go b/storage/volume/core_test.go index 5e18665e2..faff2de60 100644 --- a/storage/volume/core_test.go +++ b/storage/volume/core_test.go @@ -8,8 +8,8 @@ import ( "strconv" "testing" + "github.com/alibaba/pouch/pkg/errtypes" "github.com/alibaba/pouch/storage/volume/driver" - volerr "github.com/alibaba/pouch/storage/volume/error" "github.com/alibaba/pouch/storage/volume/types" ) @@ -39,7 +39,7 @@ func TestCreateVolume(t *testing.T) { driver.Register(driver.NewFakeDriver(volumeDriverName)) defer driver.Unregister(volumeDriverName) - v, err := core.CreateVolume(types.VolumeID{Name: "test1", Driver: volumeDriverName}) + v, err := core.CreateVolume(types.VolumeContext{Name: "test1", Driver: volumeDriverName}) if err != nil { t.Fatalf("create volume error: %v", err) } @@ -51,7 +51,7 @@ func TestCreateVolume(t *testing.T) { t.Fatalf("expect volume driver is %s, but got %s", volumeDriverName, v.Driver()) } - _, err = core.CreateVolume(types.VolumeID{Name: "none", Driver: "none"}) + _, err = core.CreateVolume(types.VolumeContext{Name: "none", Driver: "none"}) if err == nil { t.Fatal("expect get driver not found error, but err is nil") } @@ -74,7 +74,7 @@ func TestGetVolume(t *testing.T) { // add one volume and get driverName1 := "fake1" volumeName1 := "test1" - vID1 := types.VolumeID{Name: volumeName1, Driver: driverName1} + vID1 := types.VolumeContext{Name: volumeName1, Driver: driverName1} driver.Register(driver.NewFakeDriver(driverName1)) defer driver.Unregister(driverName1) @@ -82,7 +82,7 @@ func TestGetVolume(t *testing.T) { if v0 != nil { t.Fatalf("expect get volume nil, but got a volume with name %s", v0.Name) } - if err0 != volerr.ErrVolumeNotFound { + if !errtypes.IsVolumeNotFound(err0) { if err0 == nil { t.Fatal("expect get volume not found error, but err is nil") } else { @@ -107,7 +107,7 @@ func TestGetVolume(t *testing.T) { // add two volumes and get driverName2 := "fake1" volumeName2 := "test1" - vID2 := types.VolumeID{Name: volumeName2, Driver: driverName2} + vID2 := types.VolumeContext{Name: volumeName2, Driver: driverName2} driver.Register(driver.NewFakeDriver(driverName2)) defer driver.Unregister(driverName2) @@ -158,7 +158,7 @@ func TestListVolumes(t *testing.T) { volmap := map[string]*types.Volume{} for i = 0; i < 6; i++ { volName := strconv.FormatInt(i, 10) - volid := types.VolumeID{Name: volName, Driver: driverName} + volid := types.VolumeContext{Name: volName, Driver: driverName} v, err := core.CreateVolume(volid) if err != nil { t.Fatalf("create volume error: %v", err) @@ -195,7 +195,7 @@ func TestListVolumesWithLabels(t *testing.T) { var i int64 for i = 0; i < 6; i++ { volName := strconv.FormatInt(i, 10) - volid := types.VolumeID{Name: volName, Driver: driverName, Labels: map[string]string{fmt.Sprintf("label-%v", i): fmt.Sprintf("value-%v", i)}} + volid := types.VolumeContext{Name: volName, Driver: driverName, Labels: map[string]string{fmt.Sprintf("label-%v", i): fmt.Sprintf("value-%v", i)}} _, err := core.CreateVolume(volid) if err != nil { t.Fatalf("create volume error: %v", err) @@ -204,7 +204,7 @@ func TestListVolumesWithLabels(t *testing.T) { testLabels := map[string]string{"test-label": "test-value"} - testVolume, err := core.CreateVolume(types.VolumeID{Name: "test-volume", Driver: driverName, Labels: testLabels}) + testVolume, err := core.CreateVolume(types.VolumeContext{Name: "test-volume", Driver: driverName, Labels: testLabels}) if err != nil { t.Fatalf("create volume error: %v", err) } @@ -237,7 +237,7 @@ func TestListVolumeName(t *testing.T) { volmap := map[string]*types.Volume{} for i = 0; i < 6; i++ { volName := strconv.FormatInt(i, 10) - volid := types.VolumeID{Name: volName, Driver: driverName} + volid := types.VolumeContext{Name: volName, Driver: driverName} v, err := core.CreateVolume(volid) if err != nil { t.Fatalf("create volume fail: %v", err) @@ -275,7 +275,7 @@ func TestListVolumeName(t *testing.T) { func TestRemoveVolume(t *testing.T) { volName1 := "vol2" driverName1 := "fake_driver12" - volid1 := types.VolumeID{Name: volName1, Driver: driverName1} + volid1 := types.VolumeContext{Name: volName1, Driver: driverName1} dir, err := ioutil.TempDir("", "TestGetVolume") if err != nil { @@ -316,7 +316,7 @@ func TestRemoveVolume(t *testing.T) { func TestVolumePath(t *testing.T) { volName1 := "vol3" driverName1 := "fake_dirver" - volid1 := types.VolumeID{Name: volName1, Driver: driverName1} + volid1 := types.VolumeContext{Name: volName1, Driver: driverName1} expectPath := path.Join("/fake/", volName1) //keep consist with the path API in fake_driver.go dir, err := ioutil.TempDir("", "TestVolumePath") @@ -370,7 +370,7 @@ func TestAttachVolume(t *testing.T) { driverName1 := "fake1" volumeName1 := "test1" - vID1 := types.VolumeID{Name: volumeName1, Driver: driverName1} + vID1 := types.VolumeContext{Name: volumeName1, Driver: driverName1} driver.Register(driver.NewFakeDriver(volumeDriverName)) defer driver.Unregister(volumeDriverName) @@ -380,7 +380,7 @@ func TestAttachVolume(t *testing.T) { if v0 != nil { t.Fatalf("expect get volume nil, but got a volume with name %s", v0.Name) } - if err0 != volerr.ErrVolumeNotFound { + if !errtypes.IsVolumeNotFound(err0) { if err0 == nil { t.Fatal("expect get volume not found error, but err is nil") } else { @@ -388,7 +388,7 @@ func TestAttachVolume(t *testing.T) { } } - core.CreateVolume(types.VolumeID{Name: "test1", Driver: volumeDriverName}) + core.CreateVolume(types.VolumeContext{Name: "test1", Driver: volumeDriverName}) v1, err1 := core.AttachVolume(vID1, extra) if err1 != nil { @@ -406,7 +406,7 @@ func TestAttachVolume(t *testing.T) { func TestDetachVolume(t *testing.T) { volName1 := "vol2" driverName1 := "fake_driver12" - volid1 := types.VolumeID{Name: volName1, Driver: driverName1} + volid1 := types.VolumeContext{Name: volName1, Driver: driverName1} extra1 := map[string]string{} dir, err := ioutil.TempDir("", "TestDetachVolume") @@ -441,7 +441,7 @@ func TestDetachVolume(t *testing.T) { } //detach a null volume - _, err = core.DetachVolume(types.VolumeID{Name: "none", Driver: "none"}, nil) + _, err = core.DetachVolume(types.VolumeContext{Name: "none", Driver: "none"}, nil) if err == nil { t.Fatal("expect get driver not found error, but err is nil") } diff --git a/storage/volume/driver/driver_interface.go b/storage/volume/driver/driver_interface.go index 5eb5f90cf..3534f2c1f 100644 --- a/storage/volume/driver/driver_interface.go +++ b/storage/volume/driver/driver_interface.go @@ -13,7 +13,7 @@ type Driver interface { StoreMode(Context) VolumeStoreMode // Create a volume. - Create(Context, types.VolumeID) (*types.Volume, error) + Create(Context, types.VolumeContext) (*types.Volume, error) // Remove a volume. Remove(Context, *types.Volume) error diff --git a/storage/volume/driver/fake_driver.go b/storage/volume/driver/fake_driver.go index 9d70e023a..101280652 100644 --- a/storage/volume/driver/fake_driver.go +++ b/storage/volume/driver/fake_driver.go @@ -29,11 +29,11 @@ func (f *FakeDriver) StoreMode(ctx Context) VolumeStoreMode { } // Create a fake volume -func (f *FakeDriver) Create(ctx Context, id types.VolumeID) (*types.Volume, error) { +func (f *FakeDriver) Create(ctx Context, id types.VolumeContext) (*types.Volume, error) { // generate the mountPath mountPath := path.Join("/fake", id.Name) - return types.NewVolumeFromID(mountPath, "", id), nil + return types.NewVolumeFromContext(mountPath, "", id), nil } // Remove a fake volume diff --git a/storage/volume/driver/remote.go b/storage/volume/driver/remote.go index 012a79e17..dd119d946 100644 --- a/storage/volume/driver/remote.go +++ b/storage/volume/driver/remote.go @@ -1,6 +1,7 @@ package driver import ( + "github.com/alibaba/pouch/pkg/utils" "github.com/alibaba/pouch/storage/plugins" "github.com/alibaba/pouch/storage/volume/types" ) @@ -33,7 +34,7 @@ func (r *remoteDriverWrapper) StoreMode(ctx Context) VolumeStoreMode { } // Create a remote volume. -func (r *remoteDriverWrapper) Create(ctx Context, id types.VolumeID) (*types.Volume, error) { +func (r *remoteDriverWrapper) Create(ctx Context, id types.VolumeContext) (*types.Volume, error) { ctx.Log.Debugf("driver wrapper [%s] creates volume: %s", r.Name(ctx), id.Name) ctx.Log.Debugf("driver wrapper gets options: %v", id.Options) @@ -47,7 +48,7 @@ func (r *remoteDriverWrapper) Create(ctx Context, id types.VolumeID) (*types.Vol mountPath = "" } - return types.NewVolumeFromID(mountPath, "", id), nil + return types.NewVolumeFromContext(mountPath, "", id), nil } // Remove a remote volume. @@ -66,9 +67,9 @@ func (r *remoteDriverWrapper) Get(ctx Context, name string) (*types.Volume, erro return nil, err } - id := types.NewVolumeID(name, r.Name(ctx)) + id := types.NewVolumeContext(name, r.Name(ctx), utils.ToStringMap(rv.Status), nil) - return types.NewVolumeFromID(rv.Mountpoint, "", id), nil + return types.NewVolumeFromContext(rv.Mountpoint, "", id), nil } // List all volumes from remote driver. @@ -83,8 +84,8 @@ func (r *remoteDriverWrapper) List(ctx Context) ([]*types.Volume, error) { var vList []*types.Volume for _, rv := range rvList { - id := types.NewVolumeID(rv.Name, r.Name(ctx)) - volume := types.NewVolumeFromID(rv.Mountpoint, "", id) + id := types.NewVolumeContext(rv.Name, r.Name(ctx), utils.ToStringMap(rv.Status), nil) + volume := types.NewVolumeFromContext(rv.Mountpoint, "", id) vList = append(vList, volume) } diff --git a/storage/volume/error/errors.go b/storage/volume/error/errors.go deleted file mode 100644 index a47b5ca38..000000000 --- a/storage/volume/error/errors.go +++ /dev/null @@ -1,73 +0,0 @@ -package error - -const ( - volumeNotFound errCode = iota - volumeExisted - storageNotFound - driverNotFound - localMetaNotFound - disableControl -) - -var ( - // ErrVolumeNotFound represents error is "volume not found" - ErrVolumeNotFound = CoreError{volumeNotFound, "volume not found"} - - // ErrVolumeExisted represents error is "volume exist" - ErrVolumeExisted = CoreError{volumeExisted, "volume exist"} - - // ErrStorageNotFound represents error is "storage not found" - ErrStorageNotFound = CoreError{storageNotFound, "storage not found"} - - // ErrDriverNotFound represents error is "driver not found" - ErrDriverNotFound = CoreError{driverNotFound, "driver not found"} - - // ErrLocalMetaNotFound represents error is "local meta not found" - ErrLocalMetaNotFound = CoreError{localMetaNotFound, "local meta not found"} - - // ErrDisableControl represents error is "disable control server" - ErrDisableControl = CoreError{disableControl, "disable control server"} -) - -type errCode int - -// CoreError represents volume core error struct. -type CoreError struct { - ec errCode - err string -} - -// Error returns core error message. -func (e CoreError) Error() string { - return e.err -} - -// IsVolumeNotFound is used to check error is volumeNotFound or not. -func (e CoreError) IsVolumeNotFound() bool { - return e.ec == volumeNotFound -} - -// IsStorageNotFound is used to check error is storageNotFound or not. -func (e CoreError) IsStorageNotFound() bool { - return e.ec == storageNotFound -} - -// IsDriverNotFound is used to check error is driverNotFound or not. -func (e CoreError) IsDriverNotFound() bool { - return e.ec == driverNotFound -} - -// IsVolumeExisted is used to check error is volumeExisted or not. -func (e CoreError) IsVolumeExisted() bool { - return e.ec == volumeExisted -} - -// IsLocalMetaNotFound is used to check error is localMetaNotFound or not. -func (e CoreError) IsLocalMetaNotFound() bool { - return e.ec == localMetaNotFound -} - -// IsDisableControl is used to check error is disableControl or not. -func (e CoreError) IsDisableControl() bool { - return e.ec == disableControl -} diff --git a/storage/volume/examples/demo/demo.go b/storage/volume/examples/demo/demo.go deleted file mode 100644 index 80581dea0..000000000 --- a/storage/volume/examples/demo/demo.go +++ /dev/null @@ -1,46 +0,0 @@ -package demo - -import ( - "path" - - "github.com/alibaba/pouch/storage/volume/driver" - "github.com/alibaba/pouch/storage/volume/types" -) - -func init() { - if err := driver.Register(&Demo{}); err != nil { - panic(err) - } -} - -// Demo represents demo volume driver. -type Demo struct { -} - -// Name returns volume driver's name. -func (d *Demo) Name(ctx driver.Context) string { - return "demo" -} - -// StoreMode returns demo driver's store mode. -func (d *Demo) StoreMode(ctx driver.Context) driver.VolumeStoreMode { - return driver.RemoteStore -} - -// Create a demo volume. -func (d *Demo) Create(ctx driver.Context, ID types.VolumeID) (*types.Volume, error) { - ctx.Log.Infof("Demo create volume: %s", ID) - return nil, nil -} - -// Remove a demo volume. -func (d *Demo) Remove(ctx driver.Context, v *types.Volume) error { - ctx.Log.Infof("Demo Remove volume: %s", v.Name) - return nil -} - -// Path returns demo volume path. -func (d *Demo) Path(ctx driver.Context, v *types.Volume) (string, error) { - ctx.Log.Infof("Demo volume path: %s", v.Name) - return path.Join("/mnt", d.Name(ctx), v.Name), nil -} diff --git a/storage/volume/modules/local/local.go b/storage/volume/modules/local/local.go index 73a48bc36..a0a6b9e9d 100644 --- a/storage/volume/modules/local/local.go +++ b/storage/volume/modules/local/local.go @@ -40,7 +40,7 @@ func (p *Local) StoreMode(ctx driver.Context) driver.VolumeStoreMode { } // Create a local volume. -func (p *Local) Create(ctx driver.Context, id types.VolumeID) (*types.Volume, error) { +func (p *Local) Create(ctx driver.Context, id types.VolumeContext) (*types.Volume, error) { ctx.Log.Debugf("Local create volume: %s", id.Name) dataPath := defaultDataPath @@ -55,16 +55,24 @@ func (p *Local) Create(ctx driver.Context, id types.VolumeID) (*types.Volume, er // parse the mount path. if dir, ok := id.Options["mount"]; ok { - mountPath = path.Join(dir, id.Name) + mountPath = dir } // parse the size. - if value, ok := id.Options["opt.size"]; ok { - sizeInt, err := bytefmt.ToMegabytes(value) + s := "" + for _, k := range []string{"size", "opt.size", "Size", "opt.Size"} { + var ok bool + s, ok = id.Options[k] + if ok { + break + } + } + if s != "" { + sizeInt, err := bytefmt.ToBytes(s) if err != nil { return nil, err } - size = strconv.Itoa(int(sizeInt)) + "M" + size = strconv.Itoa(int(sizeInt)) } // create the volume path @@ -76,7 +84,7 @@ func (p *Local) Create(ctx driver.Context, id types.VolumeID) (*types.Volume, er return nil, fmt.Errorf("mount path is not a dir %s", mountPath) } - return types.NewVolumeFromID(mountPath, size, id), nil + return types.NewVolumeFromContext(mountPath, size, id), nil } // Remove a local volume. @@ -97,13 +105,13 @@ func (p *Local) Path(ctx driver.Context, v *types.Volume) (string, error) { mountPath := v.Option("mount") if mountPath == "" { - mountPath = defaultDataPath + mountPath = path.Join(defaultDataPath, v.Name) if p.DataPath != "" { - mountPath = p.DataPath + mountPath = path.Join(p.DataPath, v.Name) } } - return path.Join(mountPath, v.Name), nil + return mountPath, nil } // Options returns local volume's options. diff --git a/storage/volume/modules/tmpfs/tmpfs.go b/storage/volume/modules/tmpfs/tmpfs.go index eecbece1b..cec15d43a 100644 --- a/storage/volume/modules/tmpfs/tmpfs.go +++ b/storage/volume/modules/tmpfs/tmpfs.go @@ -11,6 +11,7 @@ import ( "syscall" "time" + "github.com/alibaba/pouch/pkg/bytefmt" "github.com/alibaba/pouch/pkg/utils" "github.com/alibaba/pouch/storage/volume/driver" "github.com/alibaba/pouch/storage/volume/types" @@ -41,13 +42,31 @@ func (p *Tmpfs) StoreMode(ctx driver.Context) driver.VolumeStoreMode { } // Create a tmpfs volume. -func (p *Tmpfs) Create(ctx driver.Context, id types.VolumeID) (*types.Volume, error) { +func (p *Tmpfs) Create(ctx driver.Context, id types.VolumeContext) (*types.Volume, error) { ctx.Log.Debugf("Tmpfs create volume: %s", id.Name) // parse the mount path mountPath := path.Join(dataDir, id.Name) - return types.NewVolumeFromID(mountPath, "", id), nil + // parse the size, default size is 64M * 1024 * 1024 + size := "67108864" + s := "" + for _, k := range []string{"size", "opt.size", "Size", "opt.Size"} { + var ok bool + s, ok = id.Options[k] + if ok { + break + } + } + if s != "" { + sizeInt, err := bytefmt.ToBytes(s) + if err != nil { + return nil, err + } + size = strconv.Itoa(int(sizeInt)) + } + + return types.NewVolumeFromContext(mountPath, size, id), nil } // Remove a tmpfs volume. @@ -76,10 +95,16 @@ func (p *Tmpfs) Options() map[string]types.Option { func (p *Tmpfs) Attach(ctx driver.Context, v *types.Volume) error { ctx.Log.Debugf("Tmpfs attach volume: %s", v.Name) mountPath := v.Path() - size := v.Size() reqID := v.Option("reqID") - ids := v.Option("ids") + size := v.Size() + sizeInt, err := bytefmt.ToKilobytes(size) + if err != nil { + return err + } + size = strconv.Itoa(int(sizeInt)) + + ids := v.Option("ids") if ids != "" { if !strings.Contains(ids, reqID) { ids = ids + "," + reqID diff --git a/storage/volume/types/meta/meta.go b/storage/volume/types/meta/meta.go index 2378e2a71..b2802faf5 100644 --- a/storage/volume/types/meta/meta.go +++ b/storage/volume/types/meta/meta.go @@ -26,15 +26,6 @@ type ObjectMetaAccessor interface { GetObjectMeta() Object } -// GetListMeta returns ListMeta instance. -func (meta *ListMeta) GetListMeta() List { return meta } - -// GetResourceVersion returns ListMeta's resource version. -func (meta *ListMeta) GetResourceVersion() int64 { return meta.ResourceVersion } - -// SetResourceVersion is used to set ListMeta's resource version. -func (meta *ListMeta) SetResourceVersion(version int64) { meta.ResourceVersion = version } - // GetUID returns meta's uid. func (meta *ObjectMeta) GetUID() string { return meta.UID } diff --git a/storage/volume/types/meta/types.go b/storage/volume/types/meta/types.go index 30bf04fb5..9d4761abc 100644 --- a/storage/volume/types/meta/types.go +++ b/storage/volume/types/meta/types.go @@ -7,15 +7,6 @@ import ( "k8s.io/apimachinery/pkg/labels" ) -// ListMeta represents list meta. -type ListMeta struct { - // An opaque value that represents the version of this resource. May be used for optimistic - // concurrency, change detection, and the watch operation on a resource or set of resources. - // Clients must treat these values as opaque and values may only be valid for a particular - // resource or set of resources. Only servers will generate resource versions. - ResourceVersion int64 `json:"ResourceVersion,omitempty"` -} - // ObjectPhase is a int type, specific generation of an object state. type ObjectPhase string diff --git a/storage/volume/types/selector.go b/storage/volume/types/selector.go deleted file mode 100644 index d5860b951..000000000 --- a/storage/volume/types/selector.go +++ /dev/null @@ -1,45 +0,0 @@ -package types - -import ( - log "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/selection" -) - -// Selector defines SelectorRequirement slice type. -type Selector []SelectorRequirement - -// SelectorRequirement is a selector that contains values, a key, and an operator -// that relates the key and values. -type SelectorRequirement struct { - // The label key that the selector applies to. - Key string `json:"key"` - // Represents a key's relationship to a set of values. - // Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - Operator selection.Operator `json:"operator"` - // An array of string values. If the operator is In or NotIn, - // the values array must be non-empty. If the operator is Exists or DoesNotExist, - // the values array must be empty. If the operator is Gt or Lt, the values - // array must have a single element. - Values []string `json:"values,omitempty"` -} - -// AsSelector returns selector. -func (n *Selector) AsSelector() labels.Selector { - requirements := n.AsRequirement() - return labels.NewSelector().Add(requirements...) -} - -// AsRequirement returns requirement. -func (n *Selector) AsRequirement() []labels.Requirement { - var requirements []labels.Requirement - for _, sel := range *n { - requirement, err := labels.NewRequirement(sel.Key, sel.Operator, sel.Values) - if err != nil { - log.Infof("selector sel: %v as requirement error: %v", sel, err) - continue - } - requirements = append(requirements, *requirement) - } - return requirements -} diff --git a/storage/volume/types/volume.go b/storage/volume/types/volume.go index 9b20819c5..f2c3c11f5 100644 --- a/storage/volume/types/volume.go +++ b/storage/volume/types/volume.go @@ -83,11 +83,10 @@ type VolumeConfig struct { // VolumeSpec represents volume spec. type VolumeSpec struct { - ClusterID string `json:"clusterid"` - Selector Selector `json:"selector"` - Operable bool `json:"operable"` - Backend string `json:"backend,omitempty"` - MountMode string `json:"mountMode,omitempty"` + ClusterID string `json:"clusterid"` + Operable bool `json:"operable"` + Backend string `json:"backend,omitempty"` + MountMode string `json:"mountMode,omitempty"` *VolumeConfig `json:"config,inline"` Extra map[string]string `json:"extra"` } @@ -104,12 +103,6 @@ type VolumeStatus struct { Message string `json:"message"` } -// VolumeList represents volume list. -type VolumeList struct { - meta.ListMeta `json:",inline,omitempty"` - Items []Volume `json:"Items,omitempty"` -} - // Volume defined volume struct. type Volume struct { meta.ObjectMeta `json:",inline"` @@ -147,9 +140,9 @@ func (v *Volume) Driver() string { return v.Spec.Backend } -// VolumeID return volume's identity. -func (v *Volume) VolumeID() VolumeID { - return NewVolumeID(v.Name, v.Driver()) +// VolumeContext return volume's context. +func (v *Volume) VolumeContext() VolumeContext { + return NewVolumeContext(v.Name, v.Driver(), v.Spec.Extra, v.Labels) } // Label returns volume's label. @@ -162,9 +155,19 @@ func (v *Volume) SetLabel(label, value string) { v.Labels[label] = value } -// Size returns volume's size(MB). +// Size returns volume's size(bytes). func (v *Volume) Size() string { - return v.Spec.Size + if v.Spec.Size != "" { + return v.Spec.Size + } + + for _, k := range []string{"size", "Size", "opt.size", "opt.Size"} { + if s, ok := v.Spec.Extra[k]; ok { + return s + } + } + + return "" } // FileSystem returns volume's file system. @@ -191,37 +194,41 @@ func (v *Volume) CreateTime() string { return v.CreationTimestamp.Format("2006-1-2 15:04:05") } -// VolumeID use to define the volume's identity. -type VolumeID struct { - Name string - Driver string - Options map[string]string - Labels map[string]string - Selectors map[string]string +// VolumeContext use to define the volume's identity. +type VolumeContext struct { + Name string + Driver string + Options map[string]string + Labels map[string]string } -// NewVolumeID returns VolumeID instance. -func NewVolumeID(name, driver string) VolumeID { - return VolumeID{ - Name: name, - Driver: driver, - Options: map[string]string{}, - Labels: map[string]string{}, - Selectors: map[string]string{}, +// NewVolumeContext returns VolumeContext instance. +func NewVolumeContext(name, driver string, options, labels map[string]string) VolumeContext { + if options == nil { + options = map[string]string{} + } + if labels == nil { + labels = map[string]string{} + } + return VolumeContext{ + Name: name, + Driver: driver, + Options: options, + Labels: labels, } } -// Equal check VolumeID is equal or not. -func (v VolumeID) Equal(v1 VolumeID) bool { +// Equal check VolumeContext is equal or not. +func (v VolumeContext) Equal(v1 VolumeContext) bool { return (v.Name == v1.Name) && (v.Driver == v1.Driver) } -// String return VolumeID with string. -func (v VolumeID) String() string { +// String return VolumeContext with string. +func (v VolumeContext) String() string { return fmt.Sprintf("<%s, %s>", v.Name, v.Driver) } -// Invalid is used to check VolumeID's name is valid or not. -func (v VolumeID) Invalid() bool { +// Invalid is used to check VolumeContext's name is valid or not. +func (v VolumeContext) Invalid() bool { return v.Name == "" } diff --git a/storage/volume/types/volume_util.go b/storage/volume/types/volume_util.go index 3f06120eb..947c94fc8 100644 --- a/storage/volume/types/volume_util.go +++ b/storage/volume/types/volume_util.go @@ -1,36 +1,21 @@ package types import ( - "strings" "time" "github.com/alibaba/pouch/storage/volume/types/meta" "github.com/pborman/uuid" - "k8s.io/apimachinery/pkg/selection" ) -func translateSelector(k, v string) SelectorRequirement { - values := strings.Split(v, ",") - - return SelectorRequirement{ - Key: k, - Operator: selection.In, - Values: values, - } -} - -// NewVolumeFromID will create an Volume using mountPath, size and VolumeID. -func NewVolumeFromID(mountPath, size string, id VolumeID) *Volume { +// NewVolumeFromContext will create an Volume using mountPath, size and VolumeContext. +func NewVolumeFromContext(mountPath, size string, id VolumeContext) *Volume { if id.Options == nil { id.Options = map[string]string{} } if id.Labels == nil { id.Labels = map[string]string{} } - if id.Selectors == nil { - id.Selectors = map[string]string{} - } now := time.Now() v := &Volume{ @@ -45,9 +30,8 @@ func NewVolumeFromID(mountPath, size string, id VolumeID) *Volume { ModifyTimestamp: &now, }, Spec: &VolumeSpec{ - Backend: id.Driver, - Extra: id.Options, - Selector: make(Selector, 0), + Backend: id.Driver, + Extra: id.Options, VolumeConfig: &VolumeConfig{ Size: size, }, @@ -57,10 +41,5 @@ func NewVolumeFromID(mountPath, size string, id VolumeID) *Volume { }, } - for n, selector := range id.Selectors { - requirement := translateSelector(n, strings.ToLower(selector)) - v.Spec.Selector = append(v.Spec.Selector, requirement) - } - return v } diff --git a/storage/volume/types/volume_util_test.go b/storage/volume/types/volume_util_test.go index 069aa4981..30f4116be 100644 --- a/storage/volume/types/volume_util_test.go +++ b/storage/volume/types/volume_util_test.go @@ -2,7 +2,7 @@ package types import "testing" -func TestNewVolumeFromID(t *testing.T) { +func TestNewVolumeFromContext(t *testing.T) { tests := []struct { Name string Driver string @@ -24,22 +24,22 @@ func TestNewVolumeFromID(t *testing.T) { } for _, tt := range tests { - volumeID := NewVolumeID(tt.Name, tt.Driver) - v := NewVolumeFromID(tt.MountPoint, tt.Size, volumeID) + volumeID := NewVolumeContext(tt.Name, tt.Driver, nil, nil) + v := NewVolumeFromContext(tt.MountPoint, tt.Size, volumeID) if v.Name != tt.Name { - t.Errorf("NewVolumeFromID, volume's name: (%v), want (%v)", v.Name, tt.Name) + t.Errorf("NewVolumeFromContext, volume's name: (%v), want (%v)", v.Name, tt.Name) } if v.Driver() != tt.Driver { - t.Errorf("NewVolumeFromID, volume's driver: (%v), want (%v)", v.Driver(), tt.Driver) + t.Errorf("NewVolumeFromContext, volume's driver: (%v), want (%v)", v.Driver(), tt.Driver) } if v.Path() != tt.MountPoint { - t.Errorf("NewVolumeFromID, volume's driver: (%v), want (%v)", v.Path(), tt.MountPoint) + t.Errorf("NewVolumeFromContext, volume's driver: (%v), want (%v)", v.Path(), tt.MountPoint) } if v.Size() != tt.Size { - t.Errorf("NewVolumeFromID, volume's size: (%v), want (%v)", v.Size(), tt.Size) + t.Errorf("NewVolumeFromContext, volume's size: (%v), want (%v)", v.Size(), tt.Size) } } } diff --git a/test/cli_volume_test.go b/test/cli_volume_test.go index 2badecc9e..ace9f15bb 100644 --- a/test/cli_volume_test.go +++ b/test/cli_volume_test.go @@ -60,7 +60,7 @@ func (suite *PouchVolumeSuite) TestVolumeCreateLocalDriverAndSpecifyMountPoint(c funcname = tmpname[i] } - res := command.PouchRun("volume", "create", "--name", funcname, "--driver", "local", "-o", "mount=/tmp") + res := command.PouchRun("volume", "create", "--name", funcname, "--driver", "local", "-o", "mount=/tmp/"+funcname) res.Assert(c, icmd.Success) res = command.PouchRun("volume", "inspect", funcname) @@ -71,7 +71,7 @@ func (suite *PouchVolumeSuite) TestVolumeCreateLocalDriverAndSpecifyMountPoint(c } if !strings.Contains(output, "/tmp/"+funcname) { - c.Errorf("failed to get the mountpoint, expect:/tmp/%s, acturally: %s", funcname, output) + c.Errorf("failed to get the mountpoint, expect:/tmp, acturally: %s", output) } command.PouchRun("volume", "remove", funcname).Assert(c, icmd.Success) @@ -94,7 +94,7 @@ func (suite *PouchVolumeSuite) TestVolumeCreateWithMountPointExitsFile(c *check. icmd.RunCommand("touch", "/tmp/"+funcname) err := command.PouchRun("volume", "create", "--name", funcname, - "--driver", "local", "-o", "mount=/tmp").Compare(expct) + "--driver", "local", "-o", "mount=/tmp/"+funcname).Compare(expct) defer command.PouchRun("volume", "remove", funcname) c.Assert(err, check.IsNil) @@ -233,9 +233,11 @@ func (suite *PouchVolumeSuite) TestVolumeBindReplaceMode(c *check.C) { volumeName := "volume_" + funcname command.PouchRun("volume", "create", "--name", volumeName).Assert(c, icmd.Success) - command.PouchRun("run", "-d", "-v", volumeName+":/mnt", "-v", volumeName+":/home:dr", "--name", funcname, busyboxImage, "top").Assert(c, icmd.Success) defer func() { command.PouchRun("volume", "rm", volumeName) + }() + command.PouchRun("run", "-d", "-v", volumeName+":/mnt", "-v", volumeName+":/home:dr", "--name", funcname, busyboxImage, "top").Assert(c, icmd.Success) + defer func() { command.PouchRun("rm", "-f", funcname) }() @@ -291,7 +293,7 @@ func (suite *PouchVolumeSuite) TestVolumeList(c *check.C) { } } -// TestVolumeList tests the volume list with options: size and mountpoint. +// TestVolumeListOptions tests the volume list with options: size and mountpoint. func (suite *PouchVolumeSuite) TestVolumeListOptions(c *check.C) { pc, _, _, _ := runtime.Caller(0) tmpname := strings.Split(runtime.FuncForPC(pc).Name(), ".") @@ -319,7 +321,6 @@ func (suite *PouchVolumeSuite) TestVolumeListOptions(c *check.C) { for _, line := range strings.Split(ret.Stdout(), "\n") { if strings.Contains(line, volumeName) { if !strings.Contains(line, "local") || - !strings.Contains(line, "M") || !strings.Contains(line, DefaultVolumeMountPath) { c.Errorf("list result have no driver or name or size or mountpoint, line: %s", line) break