Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add options when using volume list #1028

Merged
merged 1 commit into from
Apr 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you help to change VOLUME NAME into VOLUME_NAME to avoid others to think that NAME is another column? @rudyfly

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