Skip to content

Commit

Permalink
Merge pull request #1028 from rudyfly/volume-list
Browse files Browse the repository at this point in the history
feature: add options when using volume list
  • Loading branch information
allencloud authored Apr 3, 2018
2 parents 126105b + 19932e8 commit 800c68a
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 18 deletions.
29 changes: 24 additions & 5 deletions apis/server/volume_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (s *Server) getVolume(ctx context.Context, rw http.ResponseWriter, req *htt
Name: volume.Name,
Driver: volume.Driver(),
Mountpoint: volume.Path(),
CreatedAt: volume.CreationTimestamp.Format("2006-1-2 15:04:05"),
CreatedAt: volume.CreateTime(),
Labels: volume.Labels,
}

Expand All @@ -100,12 +100,12 @@ func (s *Server) getVolume(ctx context.Context, rw http.ResponseWriter, req *htt
func (s *Server) removeVolume(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
name := mux.Vars(req)["name"]

v, err := s.VolumeMgr.Get(ctx, name)
volume, err := s.VolumeMgr.Get(ctx, name)
if err != nil {
return err
}

ref := v.Option("ref")
ref := volume.Option("ref")
if ref != "" {
return fmt.Errorf("failed to remove volume: %s, using by: %s", name, ref)
}
Expand All @@ -124,8 +124,27 @@ func (s *Server) listVolume(ctx context.Context, rw http.ResponseWriter, req *ht
}

respVolumes := types.VolumeListResp{Volumes: []*types.VolumeInfo{}, Warnings: nil}
for _, name := range volumes {
respVolumes.Volumes = append(respVolumes.Volumes, &types.VolumeInfo{Name: name})
for _, volume := range volumes {
respVolume := &types.VolumeInfo{
Name: volume.Name,
Driver: volume.Driver(),
Mountpoint: volume.Path(),
CreatedAt: volume.CreateTime(),
Labels: volume.Labels,
}

var status map[string]interface{}
for k, v := range volume.Options() {
if k != "" && v != "" {
if status == nil {
status = make(map[string]interface{})
}
status[k] = v
}
}
respVolume.Status = status

respVolumes.Volumes = append(respVolumes.Volumes, respVolume)
}
return EncodeResponse(rw, http.StatusOK, respVolumes)
}
43 changes: 34 additions & 9 deletions cli/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ var volumeListDescription = "List volumes in pouchd. " +
// VolumeListCommand is used to implement 'volume rm' command.
type VolumeListCommand struct {
baseCommand

size bool
mountPoint bool
}

// Init initializes VolumeListCommand command.
Expand All @@ -289,7 +292,11 @@ func (v *VolumeListCommand) Init(c *Cli) {
}

// addFlags adds flags for specific command.
func (v *VolumeListCommand) addFlags() {}
func (v *VolumeListCommand) addFlags() {
flagSet := v.cmd.Flags()
flagSet.BoolVar(&v.size, "size", false, "Display volume size")
flagSet.BoolVar(&v.mountPoint, "mountpoint", false, "Display volume mountpoint")
}

// runVolumeList is the entry of VolumeListCommand command.
func (v *VolumeListCommand) runVolumeList(args []string) error {
Expand All @@ -304,10 +311,28 @@ func (v *VolumeListCommand) runVolumeList(args []string) error {
}

display := v.cli.NewTableDisplay()
display.AddRow([]string{"Name:"})

for _, v := range volumeList.Volumes {
display.AddRow([]string{v.Name})
displayHead := []string{"DRIVER", "VOLUME NAME"}
if v.size {
displayHead = append(displayHead, "SIZE")
}
if v.mountPoint {
displayHead = append(displayHead, "MOUNT POINT")
}
display.AddRow(displayHead)

for _, volume := range volumeList.Volumes {
displayLine := []string{volume.Driver, volume.Name}
if v.size {
if s, ok := volume.Status["size"]; ok {
displayLine = append(displayLine, s.(string))
} else {
displayLine = append(displayLine, "ulimit")
}
}
if v.mountPoint {
displayLine = append(displayLine, volume.Mountpoint)
}
display.AddRow(displayLine)
}

display.Flush()
Expand All @@ -318,8 +343,8 @@ func (v *VolumeListCommand) runVolumeList(args []string) error {
// volumeListExample shows examples in volume list command, and is used in auto-generated cli docs.
func volumeListExample() string {
return `$ pouch volume list
Name:
pouch-volume-1
pouch-volume-2
pouch-volume-3`
DRIVER VOLUME NAME
local pouch-volume-1
local pouch-volume-2
local pouch-volume-3`
}
6 changes: 3 additions & 3 deletions daemon/mgr/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type VolumeMgr interface {
Remove(ctx context.Context, name string) error

// List returns all volumes on this host.
List(ctx context.Context, labels map[string]string) ([]string, error)
List(ctx context.Context, labels map[string]string) ([]*types.Volume, error)

// Get returns the information of volume that specified name/id.
Get(ctx context.Context, name string) (*types.Volume, error)
Expand Down Expand Up @@ -100,7 +100,7 @@ func (vm *VolumeManager) Remove(ctx context.Context, name string) error {
}

// List returns all volumes on this host.
func (vm *VolumeManager) List(ctx context.Context, labels map[string]string) ([]string, error) {
func (vm *VolumeManager) List(ctx context.Context, labels map[string]string) ([]*types.Volume, error) {
if _, ok := labels["hostname"]; !ok {
hostname, err := os.Hostname()
if err != nil {
Expand All @@ -110,7 +110,7 @@ func (vm *VolumeManager) List(ctx context.Context, labels map[string]string) ([]
labels["hostname"] = hostname
}

return vm.core.ListVolumeName(labels)
return vm.core.ListVolumes(labels)
}

// Get returns the information of volume that specified name/id.
Expand Down
72 changes: 72 additions & 0 deletions test/cli_volume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,75 @@ func (suite *PouchVolumeSuite) TestVolumeBindReplaceMode(c *check.C) {
}
c.Assert(found, check.Equals, true)
}

// TestVolumeList tests the volume list.
func (suite *PouchVolumeSuite) TestVolumeList(c *check.C) {
pc, _, _, _ := runtime.Caller(0)
tmpname := strings.Split(runtime.FuncForPC(pc).Name(), ".")
var funcname string
for i := range tmpname {
funcname = tmpname[i]
}

volumeName := "volume_" + funcname
volumeName1 := "volume_" + funcname + "_1"
command.PouchRun("volume", "create", "--name", volumeName1, "-o", "size=1g").Assert(c, icmd.Success)
defer command.PouchRun("volume", "rm", volumeName1)

volumeName2 := "volume_" + funcname + "_2"
command.PouchRun("volume", "create", "--name", volumeName2, "-o", "size=2g").Assert(c, icmd.Success)
defer command.PouchRun("volume", "rm", volumeName2)

volumeName3 := "volume_" + funcname + "_3"
command.PouchRun("volume", "create", "--name", volumeName3, "-o", "size=3g").Assert(c, icmd.Success)
defer command.PouchRun("volume", "rm", volumeName3)

ret := command.PouchRun("volume", "list")
ret.Assert(c, icmd.Success)

for _, line := range strings.Split(ret.Stdout(), "\n") {
if strings.Contains(line, volumeName) {
if !strings.Contains(line, "local") {
c.Errorf("list result have no driver or name or size or mountpoint, line: %s", line)
break
}
}
}
}

// TestVolumeList 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(), ".")
var funcname string
for i := range tmpname {
funcname = tmpname[i]
}

volumeName := "volume_" + funcname
volumeName1 := "volume_" + funcname + "_1"
command.PouchRun("volume", "create", "--name", volumeName1, "-o", "size=1g").Assert(c, icmd.Success)
defer command.PouchRun("volume", "rm", volumeName1)

volumeName2 := "volume_" + funcname + "_2"
command.PouchRun("volume", "create", "--name", volumeName2, "-o", "size=2g").Assert(c, icmd.Success)
defer command.PouchRun("volume", "rm", volumeName2)

volumeName3 := "volume_" + funcname + "_3"
command.PouchRun("volume", "create", "--name", volumeName3, "-o", "size=3g").Assert(c, icmd.Success)
defer command.PouchRun("volume", "rm", volumeName3)

ret := command.PouchRun("volume", "list", "--size", "--mountpoint")
ret.Assert(c, icmd.Success)

for _, line := range strings.Split(ret.Stdout(), "\n") {
if strings.Contains(line, volumeName) {
if !strings.Contains(line, "local") ||
!strings.Contains(line, "g") ||
!strings.Contains(line, "/mnt/local") {
c.Errorf("list result have no driver or name or size or mountpoint, line: %s", line)
break
}
}
}
}
38 changes: 37 additions & 1 deletion volume/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,42 @@ func (c *Core) CreateVolume(id types.VolumeID) error {
return nil
}

// ListVolumes return all volumes.
// Param 'labels' use to filter the volumes, only return those you want.
func (c *Core) ListVolumes(labels map[string]string) ([]*types.Volume, error) {
var ls = make([]*types.Volume, 0)

// first, list local meta store.
list, err := c.store.List()
if err != nil {
return nil, err
}

// then, list central store.
if c.EnableControl {
url, err := c.listVolumeURL(labels)
if err != nil {
return nil, errors.Wrap(err, "List volume's name")
}

logrus.Debugf("List volume URL: %s, labels: %s", url, labels)

if err := client.New().ListKeys(url, &ls); err != nil {
return nil, errors.Wrap(err, "List volume's name")
}
}

for _, obj := range list {
v, ok := obj.(*types.Volume)
if !ok {
return nil, fmt.Errorf("failed to get volumes in store")
}
ls = append(ls, v)
}

return ls, nil
}

// 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) {
Expand All @@ -205,7 +241,7 @@ func (c *Core) ListVolumeName(labels map[string]string) ([]string, error) {
return nil, errors.Wrap(err, "List volume's name")
}

logrus.Debugf("List volume URL: %s, labels: %s", url, labels)
logrus.Debugf("List volume name URL: %s, labels: %s", url, labels)

if err := client.New().ListKeys(url, &names); err != nil {
return nil, errors.Wrap(err, "List volume's name")
Expand Down
18 changes: 18 additions & 0 deletions volume/core_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ func (c *Core) volumeURL(id ...types.VolumeID) (string, error) {
return client.JoinURL(c.BaseURL, types.APIVersion, client.VolumePath, id[0].Name)
}

func (c *Core) listVolumeURL(labels map[string]string) (string, error) {
if c.BaseURL == "" {
return "", volerr.ErrDisableControl
}
url, err := client.JoinURL(c.BaseURL, types.APIVersion, client.VolumePath)
if err != nil {
return "", err
}

querys := make([]string, 0, len(labels))
for k, v := range labels {
querys = append(querys, fmt.Sprintf("labels=%s=%s", k, v))
}

url = url + "?" + strings.Join(querys, "&")
return url, nil
}

func (c *Core) listVolumeNameURL(labels map[string]string) (string, error) {
if c.BaseURL == "" {
return "", volerr.ErrDisableControl
Expand Down
9 changes: 9 additions & 0 deletions volume/types/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@ func (v *Volume) Key() string {
return v.Name
}

//CreateTime returns the volume's create time.
func (v *Volume) CreateTime() string {
if v.CreationTimestamp == nil {
return ""
}

return v.CreationTimestamp.Format("2006-1-2 15:04:05")
}

// VolumeID use to define the volume's identity.
type VolumeID struct {
Name string
Expand Down

0 comments on commit 800c68a

Please sign in to comment.