From 4f60f79a27012220afdbcf8f2f4bb4622ca8c176 Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 11 Feb 2019 12:47:47 -0600 Subject: podman-remote volume inspect|ls add the ability to list and inspect volumes using the remote client and varlink Signed-off-by: baude --- API.md | 38 +++++++++++++++++++++++++ cmd/podman/commands_remoteclient.go | 2 ++ cmd/podman/varlink/io.podman.varlink | 17 ++++++++++++ cmd/podman/volume_inspect.go | 22 ++++++--------- cmd/podman/volume_ls.go | 42 ++++++++++++---------------- libpod/adapter/runtime.go | 54 ++++++++++++++++++++++++++++++++++++ libpod/adapter/runtime_remote.go | 54 ++++++++++++++++++++++++++++++++++++ libpod/adapter/volumes_remote.go | 33 ++++++++++++++++++++++ pkg/varlinkapi/volumes.go | 36 ++++++++++++++++++++++++ 9 files changed, 260 insertions(+), 38 deletions(-) create mode 100644 libpod/adapter/volumes_remote.go diff --git a/API.md b/API.md index 048cc51b9..54f7d0101 100755 --- a/API.md +++ b/API.md @@ -53,6 +53,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func GetVersion() string, string, string, string, string, int](#GetVersion) +[func GetVolumes(args: []string, all: bool) Volume](#GetVolumes) + [func HistoryImage(name: string) ImageHistory](#HistoryImage) [func ImageExists(name: string) int](#ImageExists) @@ -175,6 +177,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [type MoreResponse](#MoreResponse) +[type NotImplemented](#NotImplemented) + [type PodContainerErrorData](#PodContainerErrorData) [type PodCreate](#PodCreate) @@ -185,6 +189,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [type Sockets](#Sockets) +[type StringResponse](#StringResponse) + +[type Volume](#Volume) + [type VolumeCreateOpts](#VolumeCreateOpts) [type VolumeRemoveOpts](#VolumeRemoveOpts) @@ -516,6 +524,11 @@ $ varlink call unix:/run/podman/io.podman/io.podman.GetPodStats '{"name": "7f62b method GetVersion() [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [int](https://godoc.org/builtin#int) GetVersion returns version and build information of the podman service +### func GetVolumes +
+ +method GetVolumes(args: [[]string](#[]string), all: [bool](https://godoc.org/builtin#bool)) [Volume](#Volume)
+ ### func HistoryImage
@@ -1480,6 +1493,11 @@ MoreResponse is a struct for when responses from varlink requires longer output logs [[]string](#[]string) id [string](https://godoc.org/builtin#string) +### type NotImplemented + + + +comment [string](https://godoc.org/builtin#string) ### type PodContainerErrorData @@ -1557,6 +1575,26 @@ container_id [string](https://godoc.org/builtin#string) io_socket [string](https://godoc.org/builtin#string) control_socket [string](https://godoc.org/builtin#string) +### type StringResponse + + + +message [string](https://godoc.org/builtin#string) +### type Volume + + + +name [string](https://godoc.org/builtin#string) + +labels [map[string]](#map[string]) + +mountPoint [string](https://godoc.org/builtin#string) + +driver [string](https://godoc.org/builtin#string) + +options [map[string]](#map[string]) + +scope [string](https://godoc.org/builtin#string) ### type VolumeCreateOpts diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go index a656d5a29..7bdba1c19 100644 --- a/cmd/podman/commands_remoteclient.go +++ b/cmd/podman/commands_remoteclient.go @@ -36,6 +36,8 @@ func getVolumeSubCommands() []*cobra.Command { return []*cobra.Command{ _volumeCreateCommand, _volumeRmCommand, + _volumeLsCommand, + _volumeInspectCommand, } } diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index c3900ca18..03ea06dfc 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -2,6 +2,22 @@ # in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in the upstream libpod repository. interface io.podman +type Volume ( + name: string, + labels: [string]string, + mountPoint: string, + driver: string, + options: [string]string, + scope: string +) + +type NotImplemented ( + comment: string +) + +type StringResponse ( + message: string +) # ContainerChanges describes the return struct for ListContainerChanges type ContainerChanges ( @@ -1044,6 +1060,7 @@ method VolumeCreate(options: VolumeCreateOpts) -> (volumeName: string) method VolumeRemove(options: VolumeRemoveOpts) -> (volumeNames: []string) +method GetVolumes(args: []string, all: bool) -> (volumes: []Volume) # ImageNotFound means the image could not be found by the provided name or ID in local storage. error ImageNotFound (id: string) diff --git a/cmd/podman/volume_inspect.go b/cmd/podman/volume_inspect.go index a8e6f489f..928ef37d0 100644 --- a/cmd/podman/volume_inspect.go +++ b/cmd/podman/volume_inspect.go @@ -2,9 +2,8 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -39,22 +38,19 @@ func init() { } func volumeInspectCmd(c *cliconfig.VolumeInspectValues) error { - var err error + if (c.All && len(c.InputArgs) > 0) || (!c.All && len(c.InputArgs) < 1) { + return errors.New("provide one or more volume names or use --all") + } - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - opts := volumeLsOptions{ - Format: c.Format, - } - - vols, lastError := getVolumesFromContext(&c.PodmanCommand, runtime) - if lastError != nil { - logrus.Errorf("%q", lastError) + vols, err := runtime.InspectVolumes(getContext(), c) + if err != nil { + return err } - - return generateVolLsOutput(vols, opts, runtime) + return generateVolLsOutput(vols, volumeLsOptions{Format: c.Format}) } diff --git a/cmd/podman/volume_ls.go b/cmd/podman/volume_ls.go index 87b14a4b2..0edadc5ac 100644 --- a/cmd/podman/volume_ls.go +++ b/cmd/podman/volume_ls.go @@ -6,8 +6,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -71,7 +70,7 @@ func init() { } func volumeLsCmd(c *cliconfig.VolumeLsValues) error { - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -87,7 +86,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { opts.Format = genVolLsFormat(c) // Get the filter functions based on any filters set - var filterFuncs []libpod.VolumeFilter + var filterFuncs []adapter.VolumeFilter if c.Filter != "" { filters := strings.Split(c.Filter, ",") for _, f := range filters { @@ -95,7 +94,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { if len(filterSplit) < 2 { return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) } - generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1], runtime) + generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1]) if err != nil { return errors.Wrapf(err, "invalid filter") } @@ -103,13 +102,12 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { } } - volumes, err := runtime.GetAllVolumes() + volumes, err := runtime.Volumes(getContext()) if err != nil { return err } - // Get the volumes that match the filter - volsFiltered := make([]*libpod.Volume, 0, len(volumes)) + volsFiltered := make([]*adapter.Volume, 0, len(volumes)) for _, vol := range volumes { include := true for _, filter := range filterFuncs { @@ -120,7 +118,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { volsFiltered = append(volsFiltered, vol) } } - return generateVolLsOutput(volsFiltered, opts, runtime) + return generateVolLsOutput(volsFiltered, opts) } // generate the template based on conditions given @@ -206,7 +204,7 @@ func getVolTemplateOutput(lsParams []volumeLsJSONParams, opts volumeLsOptions) ( } // getVolJSONParams returns the volumes in JSON format -func getVolJSONParams(volumes []*libpod.Volume, opts volumeLsOptions, runtime *libpod.Runtime) ([]volumeLsJSONParams, error) { +func getVolJSONParams(volumes []*adapter.Volume) []volumeLsJSONParams { var lsOutput []volumeLsJSONParams for _, volume := range volumes { @@ -221,25 +219,19 @@ func getVolJSONParams(volumes []*libpod.Volume, opts volumeLsOptions, runtime *l lsOutput = append(lsOutput, params) } - return lsOutput, nil + return lsOutput } // generateVolLsOutput generates the output based on the format, JSON or Go Template, and prints it out -func generateVolLsOutput(volumes []*libpod.Volume, opts volumeLsOptions, runtime *libpod.Runtime) error { +func generateVolLsOutput(volumes []*adapter.Volume, opts volumeLsOptions) error { if len(volumes) == 0 && opts.Format != formats.JSONString { return nil } - lsOutput, err := getVolJSONParams(volumes, opts, runtime) - if err != nil { - return err - } + lsOutput := getVolJSONParams(volumes) var out formats.Writer switch opts.Format { case formats.JSONString: - if err != nil { - return errors.Wrapf(err, "unable to create JSON for volume output") - } out = formats.JSONStructArray{Output: volLsToGeneric([]volumeLsTemplateParams{}, lsOutput)} default: lsOutput, err := getVolTemplateOutput(lsOutput, opts) @@ -252,18 +244,18 @@ func generateVolLsOutput(volumes []*libpod.Volume, opts volumeLsOptions, runtime } // generateVolumeFilterFuncs returns the true if the volume matches the filter set, otherwise it returns false. -func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (func(volume *libpod.Volume) bool, error) { +func generateVolumeFilterFuncs(filter, filterValue string) (func(volume *adapter.Volume) bool, error) { switch filter { case "name": - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { return strings.Contains(v.Name(), filterValue) }, nil case "driver": - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { return v.Driver() == filterValue }, nil case "scope": - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { return v.Scope() == filterValue }, nil case "label": @@ -274,7 +266,7 @@ func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runti } else { filterValue = "" } - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { for labelKey, labelValue := range v.Labels() { if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { return true @@ -290,7 +282,7 @@ func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runti } else { filterValue = "" } - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { for labelKey, labelValue := range v.Options() { if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { return true diff --git a/libpod/adapter/runtime.go b/libpod/adapter/runtime.go index acaecf4b5..7dd845616 100644 --- a/libpod/adapter/runtime.go +++ b/libpod/adapter/runtime.go @@ -35,6 +35,14 @@ type Container struct { *libpod.Container } +// Volume ... +type Volume struct { + *libpod.Volume +} + +// VolumeFilter is for filtering volumes on the client +type VolumeFilter func(*Volume) bool + // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it func GetRuntime(c *cliconfig.PodmanCommand) (*LocalRuntime, error) { runtime, err := libpodruntime.GetRuntime(c) @@ -200,3 +208,49 @@ func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestM } return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil) } + +// InspectVolumes returns a slice of volumes based on an arg list or --all +func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) { + var ( + volumes []*libpod.Volume + err error + ) + + if c.All { + volumes, err = r.GetAllVolumes() + } else { + for _, v := range c.InputArgs { + vol, err := r.GetVolume(v) + if err != nil { + return nil, err + } + volumes = append(volumes, vol) + } + } + if err != nil { + return nil, err + } + return libpodVolumeToVolume(volumes), nil +} + +// Volumes returns a slice of localruntime volumes +func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) { + vols, err := r.GetAllVolumes() + if err != nil { + return nil, err + } + return libpodVolumeToVolume(vols), nil +} + +// libpodVolumeToVolume converts a slice of libpod volumes to a slice +// of localruntime volumes (same as libpod) +func libpodVolumeToVolume(volumes []*libpod.Volume) []*Volume { + var vols []*Volume + for _, v := range volumes { + newVol := Volume{ + v, + } + vols = append(vols, &newVol) + } + return vols +} diff --git a/libpod/adapter/runtime_remote.go b/libpod/adapter/runtime_remote.go index ab9b4501d..d0d827582 100644 --- a/libpod/adapter/runtime_remote.go +++ b/libpod/adapter/runtime_remote.go @@ -92,6 +92,18 @@ type remoteContainer struct { state *libpod.ContainerState } +type VolumeFilter func(*Volume) bool + +// Volume is embed for libpod volumes +type Volume struct { + remoteVolume +} + +type remoteVolume struct { + Runtime *LocalRuntime + config *libpod.VolumeConfig +} + // GetImages returns a slice of containerimages over a varlink connection func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) { var newImages []*ContainerImage @@ -480,3 +492,45 @@ func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestM return err } + +// InspectVolumes returns a slice of volumes based on an arg list or --all +func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) { + reply, err := iopodman.GetVolumes().Call(r.Conn, c.InputArgs, c.All) + if err != nil { + return nil, err + } + return varlinkVolumeToVolume(r, reply), nil +} + +//Volumes returns a slice of adapter.volumes based on information about libpod +// volumes over a varlink connection +func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) { + reply, err := iopodman.GetVolumes().Call(r.Conn, []string{}, true) + if err != nil { + return nil, err + } + return varlinkVolumeToVolume(r, reply), nil +} + +func varlinkVolumeToVolume(r *LocalRuntime, volumes []iopodman.Volume) []*Volume { + var vols []*Volume + for _, v := range volumes { + volumeConfig := libpod.VolumeConfig{ + Name: v.Name, + Labels: v.Labels, + MountPoint: v.MountPoint, + Driver: v.Driver, + Options: v.Options, + Scope: v.Scope, + } + n := remoteVolume{ + Runtime: r, + config: &volumeConfig, + } + newVol := Volume{ + n, + } + vols = append(vols, &newVol) + } + return vols +} diff --git a/libpod/adapter/volumes_remote.go b/libpod/adapter/volumes_remote.go new file mode 100644 index 000000000..beacd943a --- /dev/null +++ b/libpod/adapter/volumes_remote.go @@ -0,0 +1,33 @@ +// +build remoteclient + +package adapter + +// Name returns the name of the volume +func (v *Volume) Name() string { + return v.config.Name +} + +//Labels returns the labels for a volume +func (v *Volume) Labels() map[string]string { + return v.config.Labels +} + +// Driver returns the driver for the volume +func (v *Volume) Driver() string { + return v.config.Driver +} + +// Options returns the options a volume was created with +func (v *Volume) Options() map[string]string { + return v.config.Options +} + +// MountPath returns the path the volume is mounted to +func (v *Volume) MountPoint() string { + return v.config.MountPoint +} + +// Scope returns the scope for an adapter.volume +func (v *Volume) Scope() string { + return v.config.Scope +} diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go index ced394e90..d41b07065 100644 --- a/pkg/varlinkapi/volumes.go +++ b/pkg/varlinkapi/volumes.go @@ -36,3 +36,39 @@ func (i *LibpodAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.Vol } return call.ReplyVolumeRemove(deletedVolumes) } + +// GetVolumes returns all the volumes known to the remote system +func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all bool) error { + var ( + err error + reply []*libpod.Volume + volumes []iopodman.Volume + ) + if all { + reply, err = i.Runtime.GetAllVolumes() + } else { + for _, v := range args { + vol, err := i.Runtime.GetVolume(v) + if err != nil { + return err + } + reply = append(reply, vol) + } + } + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + // Build the iopodman.volume struct for the return + for _, v := range reply { + newVol := iopodman.Volume{ + Driver: v.Driver(), + Labels: v.Labels(), + MountPoint: v.MountPoint(), + Name: v.Name(), + Options: v.Options(), + Scope: v.Scope(), + } + volumes = append(volumes, newVol) + } + return call.ReplyGetVolumes(volumes) +} -- cgit v1.2.3-54-g00ecf