From ae614920bfe2510ca6d1dd6cea02bbe17ddb245c Mon Sep 17 00:00:00 2001
From: Brent Baude <bbaude@redhat.com>
Date: Sat, 21 Mar 2020 14:29:30 -0500
Subject: podmanv2 volumes

add volume commands: create, inspect, ls, prune, and rm

Signed-off-by: Brent Baude <bbaude@redhat.com>
---
 cmd/podmanV2/volumes/inspect.go         |  74 +++++++++++++++++
 cmd/podmanV2/volumes/list.go            |  98 ++++++++++++++++++++++
 cmd/podmanV2/volumes/prune.go           |  74 +++++++++++++++++
 cmd/podmanV2/volumes/rm.go              |  64 +++++++++++++++
 go.mod                                  |   1 +
 libpod/runtime_volume.go                |  16 ++--
 pkg/adapter/runtime.go                  |  18 +++-
 pkg/api/handlers/libpod/volumes.go      | 140 ++++++++++----------------------
 pkg/api/server/register_volumes.go      |  10 +--
 pkg/api/server/swagger.go               |   7 ++
 pkg/bindings/volumes/volumes.go         |  15 ++--
 pkg/domain/entities/engine_container.go |   9 +-
 pkg/domain/entities/types.go            |   8 --
 pkg/domain/entities/volumes.go          |  89 +++++++++++++++-----
 pkg/domain/filters/volumes.go           |  70 ++++++++++++++++
 pkg/domain/infra/abi/containers.go      |  20 -----
 pkg/domain/infra/abi/volumes.go         | 108 ++++++++++++++++++++++++
 pkg/domain/infra/tunnel/containers.go   |   7 --
 pkg/domain/infra/tunnel/runtime.go      |  18 ----
 pkg/domain/infra/tunnel/volumes.go      |  54 ++++++++++++
 pkg/varlinkapi/volumes.go               |  24 +++---
 21 files changed, 716 insertions(+), 208 deletions(-)
 create mode 100644 cmd/podmanV2/volumes/inspect.go
 create mode 100644 cmd/podmanV2/volumes/list.go
 create mode 100644 cmd/podmanV2/volumes/prune.go
 create mode 100644 cmd/podmanV2/volumes/rm.go
 create mode 100644 pkg/domain/filters/volumes.go

diff --git a/cmd/podmanV2/volumes/inspect.go b/cmd/podmanV2/volumes/inspect.go
new file mode 100644
index 000000000..4d9720432
--- /dev/null
+++ b/cmd/podmanV2/volumes/inspect.go
@@ -0,0 +1,74 @@
+package volumes
+
+import (
+	"encoding/json"
+	"fmt"
+	"html/template"
+	"os"
+
+	"github.com/containers/buildah/pkg/formats"
+	"github.com/containers/libpod/cmd/podmanV2/registry"
+	"github.com/containers/libpod/pkg/domain/entities"
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+	"golang.org/x/net/context"
+)
+
+var (
+	volumeInspectDescription = `Display detailed information on one or more volumes.
+
+  Use a Go template to change the format from JSON.`
+	inspectCommand = &cobra.Command{
+		Use:   "inspect [flags] VOLUME [VOLUME...]",
+		Short: "Display detailed information on one or more volumes",
+		Long:  volumeInspectDescription,
+		RunE:  inspect,
+		Example: `podman volume inspect myvol
+  podman volume inspect --all
+  podman volume inspect --format "{{.Driver}} {{.Scope}}" myvol`,
+	}
+)
+
+var (
+	inspectOpts   = entities.VolumeInspectOptions{}
+	inspectFormat string
+)
+
+func init() {
+	registry.Commands = append(registry.Commands, registry.CliCommand{
+		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+		Command: inspectCommand,
+		Parent:  volumeCmd,
+	})
+	flags := inspectCommand.Flags()
+	flags.BoolVarP(&inspectOpts.All, "all", "a", false, "Inspect all volumes")
+	flags.StringVarP(&inspectFormat, "format", "f", "json", "Format volume output using Go template")
+}
+
+func inspect(cmd *cobra.Command, args []string) error {
+	if (inspectOpts.All && len(args) > 0) || (!inspectOpts.All && len(args) < 1) {
+		return errors.New("provide one or more volume names or use --all")
+	}
+	responses, err := registry.ContainerEngine().VolumeInspect(context.Background(), args, inspectOpts)
+	if err != nil {
+		return err
+	}
+	switch inspectFormat {
+	case "", formats.JSONString:
+		jsonOut, err := json.MarshalIndent(responses, "", "     ")
+		if err != nil {
+			return errors.Wrapf(err, "error marshalling inspect JSON")
+		}
+		fmt.Println(string(jsonOut))
+	default:
+		tmpl, err := template.New("volumeInspect").Parse(inspectFormat)
+		if err != nil {
+			return err
+		}
+		if err := tmpl.Execute(os.Stdout, responses); err != nil {
+			return err
+		}
+	}
+	return nil
+
+}
diff --git a/cmd/podmanV2/volumes/list.go b/cmd/podmanV2/volumes/list.go
new file mode 100644
index 000000000..c38f78c73
--- /dev/null
+++ b/cmd/podmanV2/volumes/list.go
@@ -0,0 +1,98 @@
+package volumes
+
+import (
+	"context"
+	"html/template"
+	"io"
+	"os"
+	"strings"
+	"text/tabwriter"
+
+	"github.com/containers/libpod/cmd/podmanV2/registry"
+	"github.com/containers/libpod/pkg/domain/entities"
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+)
+
+var (
+	volumeLsDescription = `
+podman volume ls
+
+List all available volumes. The output of the volumes can be filtered
+and the output format can be changed to JSON or a user specified Go template.`
+	lsCommand = &cobra.Command{
+		Use:     "ls",
+		Aliases: []string{"list"},
+		Args:    cobra.NoArgs,
+		Short:   "List volumes",
+		Long:    volumeLsDescription,
+		RunE:    list,
+	}
+)
+
+var (
+	// Temporary struct to hold cli values.
+	cliOpts = struct {
+		Filter []string
+		Format string
+		Quiet  bool
+	}{}
+	lsOpts = entities.VolumeListOptions{}
+)
+
+func init() {
+	registry.Commands = append(registry.Commands, registry.CliCommand{
+		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+		Command: lsCommand,
+		Parent:  volumeCmd,
+	})
+	flags := lsCommand.Flags()
+	flags.StringSliceVarP(&cliOpts.Filter, "filter", "f", []string{}, "Filter volume output")
+	flags.StringVar(&cliOpts.Format, "format", "{{.Driver}}\t{{.Name}}\n", "Format volume output using Go template")
+	flags.BoolVarP(&cliOpts.Quiet, "quiet", "q", false, "Print volume output in quiet mode")
+}
+
+func list(cmd *cobra.Command, args []string) error {
+	var w io.Writer = os.Stdout
+	if cliOpts.Quiet && cmd.Flag("format").Changed {
+		return errors.New("quiet and format flags cannot be used together")
+	}
+	for _, f := range cliOpts.Filter {
+		filterSplit := strings.Split(f, "=")
+		if len(filterSplit) < 2 {
+			return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+		}
+		lsOpts.Filter[filterSplit[0]] = append(lsOpts.Filter[filterSplit[0]], filterSplit[1:]...)
+	}
+	responses, err := registry.ContainerEngine().VolumeList(context.Background(), lsOpts)
+	if err != nil {
+		return err
+	}
+	// "\t" from the command line is not being recognized as a tab
+	// replacing the string "\t" to a tab character if the user passes in "\t"
+	cliOpts.Format = strings.Replace(cliOpts.Format, `\t`, "\t", -1)
+	if cliOpts.Quiet {
+		cliOpts.Format = "{{.Name}}\n"
+	}
+	headers := "DRIVER\tVOLUME NAME\n"
+	row := cliOpts.Format
+	if !strings.HasSuffix(cliOpts.Format, "\n") {
+		row += "\n"
+	}
+	format := "{{range . }}" + row + "{{end}}"
+	if !cliOpts.Quiet && !cmd.Flag("format").Changed {
+		w = tabwriter.NewWriter(os.Stdout, 12, 2, 2, ' ', 0)
+		format = headers + format
+	}
+	tmpl, err := template.New("listVolume").Parse(format)
+	if err != nil {
+		return err
+	}
+	if err := tmpl.Execute(w, responses); err != nil {
+		return err
+	}
+	if flusher, ok := w.(interface{ Flush() error }); ok {
+		return flusher.Flush()
+	}
+	return nil
+}
diff --git a/cmd/podmanV2/volumes/prune.go b/cmd/podmanV2/volumes/prune.go
new file mode 100644
index 000000000..148065f56
--- /dev/null
+++ b/cmd/podmanV2/volumes/prune.go
@@ -0,0 +1,74 @@
+package volumes
+
+import (
+	"bufio"
+	"context"
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/containers/libpod/cmd/podmanV2/registry"
+	"github.com/containers/libpod/cmd/podmanV2/utils"
+	"github.com/containers/libpod/pkg/domain/entities"
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+)
+
+var (
+	volumePruneDescription = `Volumes that are not currently owned by a container will be removed.
+
+  The command prompts for confirmation which can be overridden with the --force flag.
+  Note all data will be destroyed.`
+	pruneCommand = &cobra.Command{
+		Use:   "prune",
+		Args:  cobra.NoArgs,
+		Short: "Remove all unused volumes",
+		Long:  volumePruneDescription,
+		RunE:  prune,
+	}
+)
+
+var (
+	pruneOptions entities.VolumePruneOptions
+)
+
+func init() {
+	registry.Commands = append(registry.Commands, registry.CliCommand{
+		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+		Command: pruneCommand,
+		Parent:  volumeCmd,
+	})
+	flags := pruneCommand.Flags()
+	flags.BoolVarP(&pruneOptions.Force, "force", "f", false, "Do not prompt for confirmation")
+}
+
+func prune(cmd *cobra.Command, args []string) error {
+	var (
+		errs utils.OutputErrors
+	)
+	// Prompt for confirmation if --force is not set
+	if !pruneOptions.Force {
+		reader := bufio.NewReader(os.Stdin)
+		fmt.Println("WARNING! This will remove all volumes not used by at least one container.")
+		fmt.Print("Are you sure you want to continue? [y/N] ")
+		answer, err := reader.ReadString('\n')
+		if err != nil {
+			return errors.Wrapf(err, "error reading input")
+		}
+		if strings.ToLower(answer)[0] != 'y' {
+			return nil
+		}
+	}
+	responses, err := registry.ContainerEngine().VolumePrune(context.Background(), pruneOptions)
+	if err != nil {
+		return err
+	}
+	for _, r := range responses {
+		if r.Err == nil {
+			fmt.Println(r.Id)
+		} else {
+			errs = append(errs, r.Err)
+		}
+	}
+	return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/volumes/rm.go b/cmd/podmanV2/volumes/rm.go
new file mode 100644
index 000000000..b019285d8
--- /dev/null
+++ b/cmd/podmanV2/volumes/rm.go
@@ -0,0 +1,64 @@
+package volumes
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/containers/libpod/cmd/podmanV2/registry"
+	"github.com/containers/libpod/cmd/podmanV2/utils"
+	"github.com/containers/libpod/pkg/domain/entities"
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+)
+
+var (
+	volumeRmDescription = `Remove one or more existing volumes.
+
+  By default only volumes that are not being used by any containers will be removed. To remove the volumes anyways, use the --force flag.`
+	rmCommand = &cobra.Command{
+		Use:     "rm [flags] VOLUME [VOLUME...]",
+		Aliases: []string{"remove"},
+		Short:   "Remove one or more volumes",
+		Long:    volumeRmDescription,
+		RunE:    rm,
+		Example: `podman volume rm myvol1 myvol2
+  podman volume rm --all
+  podman volume rm --force myvol`,
+	}
+)
+
+var (
+	rmOptions = entities.VolumeRmOptions{}
+)
+
+func init() {
+	registry.Commands = append(registry.Commands, registry.CliCommand{
+		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+		Command: rmCommand,
+		Parent:  volumeCmd,
+	})
+	flags := rmCommand.Flags()
+	flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all volumes")
+	flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Remove a volume by force, even if it is being used by a container")
+}
+
+func rm(cmd *cobra.Command, args []string) error {
+	var (
+		errs utils.OutputErrors
+	)
+	if (len(args) > 0 && rmOptions.All) || (len(args) < 1 && !rmOptions.All) {
+		return errors.New("choose either one or more volumes or all")
+	}
+	responses, err := registry.ContainerEngine().VolumeRm(context.Background(), args, rmOptions)
+	if err != nil {
+		return err
+	}
+	for _, r := range responses {
+		if r.Err == nil {
+			fmt.Println(r.Id)
+		} else {
+			errs = append(errs, r.Err)
+		}
+	}
+	return errs.PrintErrors()
+}
diff --git a/go.mod b/go.mod
index ce603491e..a4f450418 100644
--- a/go.mod
+++ b/go.mod
@@ -59,6 +59,7 @@ require (
 	github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b
 	github.com/vishvananda/netlink v1.1.0
 	golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
+	golang.org/x/net v0.0.0-20191004110552-13f9640d40b9
 	golang.org/x/sync v0.0.0-20190423024810-112230192c58
 	golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2
 	gopkg.in/yaml.v2 v2.2.8
diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go
index efc3c5bd9..d522ffb6c 100644
--- a/libpod/runtime_volume.go
+++ b/libpod/runtime_volume.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/containers/libpod/libpod/define"
 	"github.com/containers/libpod/libpod/events"
+	"github.com/containers/libpod/pkg/domain/entities"
 	"github.com/pkg/errors"
 )
 
@@ -35,7 +36,6 @@ func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool) error
 			return nil
 		}
 	}
-
 	return r.removeVolume(ctx, v, force)
 }
 
@@ -130,26 +130,24 @@ func (r *Runtime) GetAllVolumes() ([]*Volume, error) {
 }
 
 // PruneVolumes removes unused volumes from the system
-func (r *Runtime) PruneVolumes(ctx context.Context) ([]string, []error) {
+func (r *Runtime) PruneVolumes(ctx context.Context) ([]*entities.VolumePruneReport, error) {
 	var (
-		prunedIDs   []string
-		pruneErrors []error
+		reports []*entities.VolumePruneReport
 	)
 	vols, err := r.GetAllVolumes()
 	if err != nil {
-		pruneErrors = append(pruneErrors, err)
-		return nil, pruneErrors
+		return nil, err
 	}
 
 	for _, vol := range vols {
 		if err := r.RemoveVolume(ctx, vol, false); err != nil {
 			if errors.Cause(err) != define.ErrVolumeBeingUsed && errors.Cause(err) != define.ErrVolumeRemoved {
-				pruneErrors = append(pruneErrors, err)
+				reports = append(reports, &entities.VolumePruneReport{Id: vol.Name(), Err: err})
 			}
 			continue
 		}
 		vol.newVolumeEvent(events.Prune)
-		prunedIDs = append(prunedIDs, vol.Name())
+		reports = append(reports, &entities.VolumePruneReport{Id: vol.Name()})
 	}
-	return prunedIDs, pruneErrors
+	return reports, nil
 }
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index dfe6b7f07..7817a1f98 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -347,7 +347,23 @@ func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, opti
 
 // PruneVolumes is a wrapper function for libpod PruneVolumes
 func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
-	return r.Runtime.PruneVolumes(ctx)
+	var (
+		vids []string
+		errs []error
+	)
+	reports, err := r.Runtime.PruneVolumes(ctx)
+	if err != nil {
+		errs = append(errs, err)
+		return vids, errs
+	}
+	for _, r := range reports {
+		if r.Err == nil {
+			vids = append(vids, r.Id)
+		} else {
+			errs = append(errs, r.Err)
+		}
+	}
+	return vids, errs
 }
 
 // SaveImage is a wrapper function for saving an image to the local filesystem
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index 06ca1d225..e61d272f4 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -3,16 +3,15 @@ package libpod
 import (
 	"encoding/json"
 	"net/http"
-	"strings"
 
 	"github.com/containers/libpod/cmd/podman/shared"
 	"github.com/containers/libpod/libpod"
 	"github.com/containers/libpod/libpod/define"
 	"github.com/containers/libpod/pkg/api/handlers/utils"
 	"github.com/containers/libpod/pkg/domain/entities"
+	"github.com/containers/libpod/pkg/domain/filters"
 	"github.com/gorilla/schema"
 	"github.com/pkg/errors"
-	log "github.com/sirupsen/logrus"
 )
 
 func CreateVolume(w http.ResponseWriter, r *http.Request) {
@@ -65,14 +64,14 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	volResponse := entities.VolumeConfigResponse{
-		Name:        config.Name,
-		Labels:      config.Labels,
-		Driver:      config.Driver,
-		MountPoint:  config.MountPoint,
-		CreatedTime: config.CreatedTime,
-		Options:     config.Options,
-		UID:         config.UID,
-		GID:         config.GID,
+		Name:       config.Name,
+		Driver:     config.Driver,
+		Mountpoint: config.MountPoint,
+		CreatedAt:  config.CreatedTime,
+		Labels:     config.Labels,
+		Options:    config.Options,
+		UID:        config.UID,
+		GID:        config.GID,
 	}
 	utils.WriteResponse(w, http.StatusOK, volResponse)
 }
@@ -85,21 +84,27 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) {
 	vol, err := runtime.GetVolume(name)
 	if err != nil {
 		utils.VolumeNotFound(w, name, err)
+		return
 	}
-	inspect, err := vol.Inspect()
-	if err != nil {
-		utils.InternalServerError(w, err)
+	volResponse := entities.VolumeConfigResponse{
+		Name:       vol.Name(),
+		Driver:     vol.Driver(),
+		Mountpoint: vol.MountPoint(),
+		CreatedAt:  vol.CreatedTime(),
+		Labels:     vol.Labels(),
+		Scope:      vol.Scope(),
+		Options:    vol.Options(),
+		UID:        vol.UID(),
+		GID:        vol.GID(),
 	}
-	utils.WriteResponse(w, http.StatusOK, inspect)
+	utils.WriteResponse(w, http.StatusOK, volResponse)
 }
 
 func ListVolumes(w http.ResponseWriter, r *http.Request) {
 	var (
 		decoder       = r.Context().Value("decoder").(*schema.Decoder)
-		err           error
 		runtime       = r.Context().Value("runtime").(*libpod.Runtime)
-		volumeConfigs []*libpod.VolumeConfig
-		volumeFilters []libpod.VolumeFilter
+		volumeConfigs []*entities.VolumeListReport
 	)
 	query := struct {
 		Filters map[string][]string `schema:"filters"`
@@ -113,25 +118,30 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if len(query.Filters) > 0 {
-		volumeFilters, err = generateVolumeFilters(query.Filters)
-		if err != nil {
-			utils.InternalServerError(w, err)
-			return
-		}
+	volumeFilters, err := filters.GenerateVolumeFilters(query.Filters)
+	if err != nil {
+		utils.InternalServerError(w, err)
+		return
 	}
+
 	vols, err := runtime.Volumes(volumeFilters...)
 	if err != nil {
 		utils.InternalServerError(w, err)
 		return
 	}
 	for _, v := range vols {
-		config, err := v.Config()
-		if err != nil {
-			utils.InternalServerError(w, err)
-			return
+		config := entities.VolumeConfigResponse{
+			Name:       v.Name(),
+			Driver:     v.Driver(),
+			Mountpoint: v.MountPoint(),
+			CreatedAt:  v.CreatedTime(),
+			Labels:     v.Labels(),
+			Scope:      v.Scope(),
+			Options:    v.Options(),
+			UID:        v.UID(),
+			GID:        v.GID(),
 		}
-		volumeConfigs = append(volumeConfigs, config)
+		volumeConfigs = append(volumeConfigs, &entities.VolumeListReport{VolumeConfigResponse: config})
 	}
 	utils.WriteResponse(w, http.StatusOK, volumeConfigs)
 }
@@ -140,14 +150,10 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) {
 	var (
 		runtime = r.Context().Value("runtime").(*libpod.Runtime)
 	)
-	pruned, errs := runtime.PruneVolumes(r.Context())
-	if errs != nil {
-		if len(errs) > 1 {
-			for _, err := range errs {
-				log.Infof("Request Failed(%s): %s", http.StatusText(http.StatusInternalServerError), err.Error())
-			}
-		}
-		utils.InternalServerError(w, errs[len(errs)-1])
+	pruned, err := runtime.PruneVolumes(r.Context())
+	if err != nil {
+		utils.InternalServerError(w, err)
+		return
 	}
 	utils.WriteResponse(w, http.StatusOK, pruned)
 }
@@ -184,65 +190,3 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) {
 	}
 	utils.WriteResponse(w, http.StatusNoContent, "")
 }
-
-func generateVolumeFilters(filters map[string][]string) ([]libpod.VolumeFilter, error) {
-	var vf []libpod.VolumeFilter
-	for filter, v := range filters {
-		for _, val := range v {
-			switch filter {
-			case "name":
-				nameVal := val
-				vf = append(vf, func(v *libpod.Volume) bool {
-					return nameVal == v.Name()
-				})
-			case "driver":
-				driverVal := val
-				vf = append(vf, func(v *libpod.Volume) bool {
-					return v.Driver() == driverVal
-				})
-			case "scope":
-				scopeVal := val
-				vf = append(vf, func(v *libpod.Volume) bool {
-					return v.Scope() == scopeVal
-				})
-			case "label":
-				filterArray := strings.SplitN(val, "=", 2)
-				filterKey := filterArray[0]
-				var filterVal string
-				if len(filterArray) > 1 {
-					filterVal = filterArray[1]
-				} else {
-					filterVal = ""
-				}
-				vf = append(vf, func(v *libpod.Volume) bool {
-					for labelKey, labelValue := range v.Labels() {
-						if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) {
-							return true
-						}
-					}
-					return false
-				})
-			case "opt":
-				filterArray := strings.SplitN(val, "=", 2)
-				filterKey := filterArray[0]
-				var filterVal string
-				if len(filterArray) > 1 {
-					filterVal = filterArray[1]
-				} else {
-					filterVal = ""
-				}
-				vf = append(vf, func(v *libpod.Volume) bool {
-					for labelKey, labelValue := range v.Options() {
-						if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) {
-							return true
-						}
-					}
-					return false
-				})
-			default:
-				return nil, errors.Errorf("%q is in an invalid volume filter", filter)
-			}
-		}
-	}
-	return vf, nil
-}
diff --git a/pkg/api/server/register_volumes.go b/pkg/api/server/register_volumes.go
index 2cf249cc3..93b972b6b 100644
--- a/pkg/api/server/register_volumes.go
+++ b/pkg/api/server/register_volumes.go
@@ -53,8 +53,8 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
 	// produces:
 	// - application/json
 	// responses:
-	//   '204':
-	//     description: no error
+	//   '200':
+	//      "$ref": "#/responses/VolumePruneResponse"
 	//   '500':
 	//      "$ref": "#/responses/InternalError"
 	r.Handle(VersionedPath("/libpod/volumes/prune"), s.APIHandler(libpod.PruneVolumes)).Methods(http.MethodPost)
@@ -71,11 +71,11 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
 	// - application/json
 	// responses:
 	//   '200':
-	//       "$ref": "#/responses/InspectVolumeResponse"
+	//     "$ref": "#/responses/VolumeCreateResponse"
 	//   '404':
-	//       "$ref": "#/responses/NoSuchVolume"
+	//     "$ref": "#/responses/NoSuchVolume"
 	//   '500':
-	//      "$ref": "#/responses/InternalError"
+	//     "$ref": "#/responses/InternalError"
 	r.Handle(VersionedPath("/libpod/volumes/{name}/json"), s.APIHandler(libpod.InspectVolume)).Methods(http.MethodGet)
 	// swagger:operation DELETE /libpod/volumes/{name} volumes removeVolume
 	// ---
diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go
index 2e1a269f2..50ce42b3b 100644
--- a/pkg/api/server/swagger.go
+++ b/pkg/api/server/swagger.go
@@ -151,6 +151,13 @@ type ok struct {
 	}
 }
 
+// Volume prune response
+// swagger:response VolumePruneResponse
+type swagVolumePruneResponse struct {
+	// in:body
+	Body []entities.VolumePruneReport
+}
+
 // Volume create response
 // swagger:response VolumeCreateResponse
 type swagVolumeCreateResponse struct {
diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go
index a2164e0af..cef9246cb 100644
--- a/pkg/bindings/volumes/volumes.go
+++ b/pkg/bindings/volumes/volumes.go
@@ -7,7 +7,6 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/containers/libpod/libpod"
 	"github.com/containers/libpod/pkg/bindings"
 	"github.com/containers/libpod/pkg/domain/entities"
 	jsoniter "github.com/json-iterator/go"
@@ -35,9 +34,9 @@ func Create(ctx context.Context, config entities.VolumeCreateOptions) (*entities
 }
 
 // Inspect returns low-level information about a volume.
-func Inspect(ctx context.Context, nameOrID string) (*libpod.InspectVolumeData, error) {
+func Inspect(ctx context.Context, nameOrID string) (*entities.VolumeConfigResponse, error) {
 	var (
-		inspect libpod.InspectVolumeData
+		inspect entities.VolumeConfigResponse
 	)
 	conn, err := bindings.GetClient(ctx)
 	if err != nil {
@@ -52,9 +51,9 @@ func Inspect(ctx context.Context, nameOrID string) (*libpod.InspectVolumeData, e
 
 // List returns the configurations for existing volumes in the form of a slice.  Optionally, filters
 // can be used to refine the list of volumes.
-func List(ctx context.Context, filters map[string][]string) ([]*libpod.VolumeConfig, error) {
+func List(ctx context.Context, filters map[string][]string) ([]*entities.VolumeListReport, error) {
 	var (
-		vols []*libpod.VolumeConfig
+		vols []*entities.VolumeListReport
 	)
 	conn, err := bindings.GetClient(ctx)
 	if err != nil {
@@ -76,9 +75,9 @@ func List(ctx context.Context, filters map[string][]string) ([]*libpod.VolumeCon
 }
 
 // Prune removes unused volumes from the local filesystem.
-func Prune(ctx context.Context) ([]string, error) {
+func Prune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
 	var (
-		pruned []string
+		pruned []*entities.VolumePruneReport
 	)
 	conn, err := bindings.GetClient(ctx)
 	if err != nil {
@@ -86,7 +85,7 @@ func Prune(ctx context.Context) ([]string, error) {
 	}
 	response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", nil)
 	if err != nil {
-		return pruned, err
+		return nil, err
 	}
 	return pruned, response.Process(&pruned)
 }
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 2887f0b51..2efdbd602 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -5,7 +5,6 @@ import (
 )
 
 type ContainerEngine interface {
-	ContainerPrune(ctx context.Context) (*ContainerPruneReport, error)
 	ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
 	ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
 	ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
@@ -14,10 +13,10 @@ type ContainerEngine interface {
 	ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
 	ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
 	ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
-	PodDelete(ctx context.Context, opts PodPruneOptions) (*PodDeleteReport, error)
 	PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
-	PodPrune(ctx context.Context) (*PodPruneReport, error)
 	VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error)
-	VolumeDelete(ctx context.Context, opts VolumeDeleteOptions) (*VolumeDeleteReport, error)
-	VolumePrune(ctx context.Context) (*VolumePruneReport, error)
+	VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error)
+	VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
+	VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error)
+	VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
 }
diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go
index 6f947dc4d..e7757a74b 100644
--- a/pkg/domain/entities/types.go
+++ b/pkg/domain/entities/types.go
@@ -13,13 +13,5 @@ type Report struct {
 	Err map[string]error
 }
 
-type ContainerDeleteOptions struct{}
-type ContainerDeleteReport struct{ Report }
-type ContainerPruneReport struct{ Report }
-
 type PodDeleteReport struct{ Report }
 type PodPruneOptions struct{}
-type PodPruneReport struct{ Report }
-type VolumeDeleteOptions struct{}
-type VolumeDeleteReport struct{ Report }
-type VolumePruneReport struct{ Report }
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index ad12d0d01..23c066083 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -1,6 +1,8 @@
 package entities
 
-import "time"
+import (
+	"time"
+)
 
 // swagger:model VolumeCreate
 type VolumeCreateOptions struct {
@@ -20,22 +22,71 @@ type IdOrNameResponse struct {
 }
 
 type VolumeConfigResponse struct {
-	// Name of the volume.
-	Name   string            `json:"name"`
-	Labels map[string]string `json:"labels"`
-	// The volume driver. Empty string or local does not activate a volume
-	// driver, all other volumes will.
-	Driver string `json:"volumeDriver"`
-	// The location the volume is mounted at.
-	MountPoint string `json:"mountPoint"`
-	// Time the volume was created.
-	CreatedTime time.Time `json:"createdAt,omitempty"`
-	// Options to pass to the volume driver. For the local driver, this is
-	// a list of mount options. For other drivers, they are passed to the
-	// volume driver handling the volume.
-	Options map[string]string `json:"volumeOptions,omitempty"`
-	// UID the volume will be created as.
-	UID int `json:"uid"`
-	// GID the volume will be created as.
-	GID int `json:"gid"`
+	// Name is the name of the volume.
+	Name string `json:"Name"`
+	// Driver is the driver used to create the volume.
+	// This will be properly implemented in a future version.
+	Driver string `json:"Driver"`
+	// Mountpoint is the path on the host where the volume is mounted.
+	Mountpoint string `json:"Mountpoint"`
+	// CreatedAt is the date and time the volume was created at. This is not
+	// stored for older Libpod volumes; if so, it will be omitted.
+	CreatedAt time.Time `json:"CreatedAt,omitempty"`
+	// Status is presently unused and provided only for Docker compatibility.
+	// In the future it will be used to return information on the volume's
+	// current state.
+	Status map[string]string `json:"Status,omitempty"`
+	// Labels includes the volume's configured labels, key:value pairs that
+	// can be passed during volume creation to provide information for third
+	// party tools.
+	Labels map[string]string `json:"Labels"`
+	// Scope is unused and provided solely for Docker compatibility. It is
+	// unconditionally set to "local".
+	Scope string `json:"Scope"`
+	// Options is a set of options that were used when creating the volume.
+	// It is presently not used.
+	Options map[string]string `json:"Options"`
+	// UID is the UID that the volume was created with.
+	UID int `json:"UID,omitempty"`
+	// GID is the GID that the volume was created with.
+	GID int `json:"GID,omitempty"`
+	// Anonymous indicates that the volume was created as an anonymous
+	// volume for a specific container, and will be be removed when any
+	// container using it is removed.
+	Anonymous bool `json:"Anonymous,omitempty"`
+}
+
+type VolumeRmOptions struct {
+	All   bool
+	Force bool
+}
+
+type VolumeRmReport struct {
+	Err error
+	Id  string
+}
+
+type VolumeInspectOptions struct {
+	All bool
+}
+
+type VolumeInspectReport struct {
+	*VolumeConfigResponse
+}
+
+type VolumePruneOptions struct {
+	Force bool
+}
+
+type VolumePruneReport struct {
+	Err error
+	Id  string
+}
+
+type VolumeListOptions struct {
+	Filter map[string][]string
+}
+
+type VolumeListReport struct {
+	VolumeConfigResponse
 }
diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go
new file mode 100644
index 000000000..f97c3f570
--- /dev/null
+++ b/pkg/domain/filters/volumes.go
@@ -0,0 +1,70 @@
+package filters
+
+import (
+	"strings"
+
+	"github.com/containers/libpod/libpod"
+	"github.com/pkg/errors"
+)
+
+func GenerateVolumeFilters(filters map[string][]string) ([]libpod.VolumeFilter, error) {
+	var vf []libpod.VolumeFilter
+	for filter, v := range filters {
+		for _, val := range v {
+			switch filter {
+			case "name":
+				nameVal := val
+				vf = append(vf, func(v *libpod.Volume) bool {
+					return nameVal == v.Name()
+				})
+			case "driver":
+				driverVal := val
+				vf = append(vf, func(v *libpod.Volume) bool {
+					return v.Driver() == driverVal
+				})
+			case "scope":
+				scopeVal := val
+				vf = append(vf, func(v *libpod.Volume) bool {
+					return v.Scope() == scopeVal
+				})
+			case "label":
+				filterArray := strings.SplitN(val, "=", 2)
+				filterKey := filterArray[0]
+				var filterVal string
+				if len(filterArray) > 1 {
+					filterVal = filterArray[1]
+				} else {
+					filterVal = ""
+				}
+				vf = append(vf, func(v *libpod.Volume) bool {
+					for labelKey, labelValue := range v.Labels() {
+						if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) {
+							return true
+						}
+					}
+					return false
+				})
+			case "opt":
+				filterArray := strings.SplitN(val, "=", 2)
+				filterKey := filterArray[0]
+				var filterVal string
+				if len(filterArray) > 1 {
+					filterVal = filterArray[1]
+				} else {
+					filterVal = ""
+				}
+				vf = append(vf, func(v *libpod.Volume) bool {
+					for labelKey, labelValue := range v.Options() {
+						if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) {
+							return true
+						}
+					}
+					return false
+				})
+			default:
+				return nil, errors.Errorf("%q is in an invalid volume filter", filter)
+			}
+		}
+	}
+	return vf, nil
+}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 47deb010a..a3da310c2 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -239,23 +239,3 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
 	}
 	return reports, nil
 }
-
-func (ic *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
-	panic("implement me")
-}
-
-func (ic *ContainerEngine) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) {
-	panic("implement me")
-}
-
-func (ic *ContainerEngine) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) {
-	panic("implement me")
-}
-
-func (ic *ContainerEngine) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) {
-	panic("implement me")
-}
-
-func (ic *ContainerEngine) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) {
-	panic("implement me")
-}
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index 0783af441..0cc20474e 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -7,7 +7,9 @@ import (
 
 	"github.com/containers/libpod/libpod"
 	"github.com/containers/libpod/pkg/domain/entities"
+	"github.com/containers/libpod/pkg/domain/filters"
 	"github.com/containers/libpod/pkg/domain/infra/abi/parse"
+	"github.com/pkg/errors"
 )
 
 func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IdOrNameResponse, error) {
@@ -36,3 +38,109 @@ func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.Volum
 	}
 	return &entities.IdOrNameResponse{IdOrName: vol.Name()}, nil
 }
+
+func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, opts entities.VolumeRmOptions) ([]*entities.VolumeRmReport, error) {
+	var (
+		err     error
+		reports []*entities.VolumeRmReport
+		vols    []*libpod.Volume
+	)
+	if opts.All {
+		vols, err = ic.Libpod.Volumes()
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		for _, id := range namesOrIds {
+			vol, err := ic.Libpod.LookupVolume(id)
+			if err != nil {
+				reports = append(reports, &entities.VolumeRmReport{
+					Err: err,
+					Id:  id,
+				})
+				continue
+			}
+			vols = append(vols, vol)
+		}
+	}
+	for _, vol := range vols {
+		reports = append(reports, &entities.VolumeRmReport{
+			Err: ic.Libpod.RemoveVolume(ctx, vol, opts.Force),
+			Id:  vol.Name(),
+		})
+	}
+	return reports, nil
+}
+
+func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) {
+	var (
+		err     error
+		reports []*entities.VolumeInspectReport
+		vols    []*libpod.Volume
+	)
+
+	// Note: as with previous implementation, a single failure here
+	// results a return.
+	if opts.All {
+		vols, err = ic.Libpod.GetAllVolumes()
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		for _, v := range namesOrIds {
+			vol, err := ic.Libpod.LookupVolume(v)
+			if err != nil {
+				return nil, errors.Wrapf(err, "error inspecting volume %s", v)
+			}
+			vols = append(vols, vol)
+		}
+	}
+	for _, v := range vols {
+		config := entities.VolumeConfigResponse{
+			Name:       v.Name(),
+			Driver:     v.Driver(),
+			Mountpoint: v.MountPoint(),
+			CreatedAt:  v.CreatedTime(),
+			Labels:     v.Labels(),
+			Scope:      v.Scope(),
+			Options:    v.Options(),
+			UID:        v.UID(),
+			GID:        v.GID(),
+		}
+		reports = append(reports, &entities.VolumeInspectReport{&config})
+	}
+	return reports, nil
+}
+
+func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
+	return ic.Libpod.PruneVolumes(ctx)
+}
+
+func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) {
+	var (
+		reports []*entities.VolumeListReport
+	)
+	volumeFilters, err := filters.GenerateVolumeFilters(opts.Filter)
+	if err != nil {
+		return nil, err
+	}
+	vols, err := ic.Libpod.Volumes(volumeFilters...)
+	if err != nil {
+		return nil, err
+	}
+	for _, v := range vols {
+		config := entities.VolumeConfigResponse{
+			Name:       v.Name(),
+			Driver:     v.Driver(),
+			Mountpoint: v.MountPoint(),
+			CreatedAt:  v.CreatedTime(),
+			Labels:     v.Labels(),
+			Scope:      v.Scope(),
+			Options:    v.Options(),
+			UID:        v.UID(),
+			GID:        v.GID(),
+		}
+		reports = append(reports, &entities.VolumeListReport{config})
+	}
+	return reports, nil
+}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 21f62df6b..a8ecff41b 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -33,13 +33,6 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin
 	return responses, nil
 }
 
-func (r *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) {
-	panic("implement me")
-}
-
-func (r *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
-	panic("implement me")
-}
 func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) {
 	var (
 		reports []*entities.PauseUnpauseReport
diff --git a/pkg/domain/infra/tunnel/runtime.go b/pkg/domain/infra/tunnel/runtime.go
index eb9b34e4a..c111f99e9 100644
--- a/pkg/domain/infra/tunnel/runtime.go
+++ b/pkg/domain/infra/tunnel/runtime.go
@@ -2,8 +2,6 @@ package tunnel
 
 import (
 	"context"
-
-	"github.com/containers/libpod/pkg/domain/entities"
 )
 
 // Image-related runtime using an ssh-tunnel to utilize Podman service
@@ -15,19 +13,3 @@ type ImageEngine struct {
 type ContainerEngine struct {
 	ClientCxt context.Context
 }
-
-func (r *ContainerEngine) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) {
-	panic("implement me")
-}
-
-func (r *ContainerEngine) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) {
-	panic("implement me")
-}
-
-func (r *ContainerEngine) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) {
-	panic("implement me")
-}
-
-func (r *ContainerEngine) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) {
-	panic("implement me")
-}
diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
index 49cf6a2f6..e48a7fa7c 100644
--- a/pkg/domain/infra/tunnel/volumes.go
+++ b/pkg/domain/infra/tunnel/volumes.go
@@ -14,3 +14,57 @@ func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.Volum
 	}
 	return &entities.IdOrNameResponse{IdOrName: response.Name}, nil
 }
+
+func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, opts entities.VolumeRmOptions) ([]*entities.VolumeRmReport, error) {
+	var (
+		reports []*entities.VolumeRmReport
+	)
+
+	if opts.All {
+		vols, err := volumes.List(ic.ClientCxt, nil)
+		if err != nil {
+			return nil, err
+		}
+		for _, v := range vols {
+			namesOrIds = append(namesOrIds, v.Name)
+		}
+	}
+	for _, id := range namesOrIds {
+		reports = append(reports, &entities.VolumeRmReport{
+			Err: volumes.Remove(ic.ClientCxt, id, &opts.Force),
+			Id:  id,
+		})
+	}
+	return reports, nil
+}
+
+func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) {
+	var (
+		reports []*entities.VolumeInspectReport
+	)
+	if opts.All {
+		vols, err := volumes.List(ic.ClientCxt, nil)
+		if err != nil {
+			return nil, err
+		}
+		for _, v := range vols {
+			namesOrIds = append(namesOrIds, v.Name)
+		}
+	}
+	for _, id := range namesOrIds {
+		data, err := volumes.Inspect(ic.ClientCxt, id)
+		if err != nil {
+			return nil, err
+		}
+		reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: data})
+	}
+	return reports, nil
+}
+
+func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
+	return volumes.Prune(ic.ClientCxt)
+}
+
+func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) {
+	return volumes.List(ic.ClientCxt, opts.Filter)
+}
diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go
index 2dddd3008..cbb4a70cc 100644
--- a/pkg/varlinkapi/volumes.go
+++ b/pkg/varlinkapi/volumes.go
@@ -105,16 +105,20 @@ func (i *LibpodAPI) InspectVolume(call iopodman.VarlinkCall, name string) error
 
 // VolumesPrune removes unused images via a varlink call
 func (i *LibpodAPI) VolumesPrune(call iopodman.VarlinkCall) error {
-	var errs []string
-	prunedNames, prunedErrors := i.Runtime.PruneVolumes(getContext())
-	if len(prunedErrors) == 0 {
-		return call.ReplyVolumesPrune(prunedNames, []string{})
+	var (
+		prunedErrors []string
+		prunedNames  []string
+	)
+	responses, err := i.Runtime.PruneVolumes(getContext())
+	if err != nil {
+		return call.ReplyVolumesPrune([]string{}, []string{err.Error()})
 	}
-
-	// We need to take the errors and capture their strings to go back over
-	// varlink
-	for _, e := range prunedErrors {
-		errs = append(errs, e.Error())
+	for _, i := range responses {
+		if i.Err == nil {
+			prunedNames = append(prunedNames, i.Id)
+		} else {
+			prunedErrors = append(prunedErrors, i.Err.Error())
+		}
 	}
-	return call.ReplyVolumesPrune(prunedNames, errs)
+	return call.ReplyVolumesPrune(prunedNames, prunedErrors)
 }
-- 
cgit v1.2.3-54-g00ecf