294 lines
8.0 KiB
Go
294 lines
8.0 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/containers/buildah/pkg/formats"
|
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
|
"github.com/containers/libpod/pkg/adapter"
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// volumeOptions is the "ls" command options
|
|
type volumeLsOptions struct {
|
|
Format string
|
|
Quiet bool
|
|
}
|
|
|
|
// volumeLsTemplateParams is the template parameters to list the volumes
|
|
type volumeLsTemplateParams struct {
|
|
Name string
|
|
Labels string
|
|
MountPoint string
|
|
Driver string
|
|
Options string
|
|
Scope string
|
|
}
|
|
|
|
// volumeLsJSONParams is the JSON parameters to list the volumes
|
|
type volumeLsJSONParams struct {
|
|
Name string `json:"name"`
|
|
Labels map[string]string `json:"labels"`
|
|
MountPoint string `json:"mountPoint"`
|
|
Driver string `json:"driver"`
|
|
Options map[string]string `json:"options"`
|
|
Scope string `json:"scope"`
|
|
}
|
|
|
|
var (
|
|
volumeLsCommand cliconfig.VolumeLsValues
|
|
|
|
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.`
|
|
_volumeLsCommand = &cobra.Command{
|
|
Use: "ls",
|
|
Aliases: []string{"list"},
|
|
Args: noSubArgs,
|
|
Short: "List volumes",
|
|
Long: volumeLsDescription,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
volumeLsCommand.InputArgs = args
|
|
volumeLsCommand.GlobalFlags = MainGlobalOpts
|
|
volumeLsCommand.Remote = remoteclient
|
|
return volumeLsCmd(&volumeLsCommand)
|
|
},
|
|
}
|
|
)
|
|
|
|
func init() {
|
|
volumeLsCommand.Command = _volumeLsCommand
|
|
volumeLsCommand.SetHelpTemplate(HelpTemplate())
|
|
volumeLsCommand.SetUsageTemplate(UsageTemplate())
|
|
flags := volumeLsCommand.Flags()
|
|
|
|
flags.StringVarP(&volumeLsCommand.Filter, "filter", "f", "", "Filter volume output")
|
|
flags.StringVar(&volumeLsCommand.Format, "format", "table {{.Driver}}\t{{.Name}}", "Format volume output using Go template")
|
|
flags.BoolVarP(&volumeLsCommand.Quiet, "quiet", "q", false, "Print volume output in quiet mode")
|
|
}
|
|
|
|
func volumeLsCmd(c *cliconfig.VolumeLsValues) error {
|
|
runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error creating libpod runtime")
|
|
}
|
|
defer runtime.DeferredShutdown(false)
|
|
|
|
opts := volumeLsOptions{
|
|
Quiet: c.Quiet,
|
|
}
|
|
opts.Format = genVolLsFormat(c)
|
|
|
|
// Get the filter functions based on any filters set
|
|
var filterFuncs []adapter.VolumeFilter
|
|
if c.Filter != "" {
|
|
filters := strings.Split(c.Filter, ",")
|
|
for _, f := range filters {
|
|
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)
|
|
}
|
|
generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1])
|
|
if err != nil {
|
|
return errors.Wrapf(err, "invalid filter")
|
|
}
|
|
filterFuncs = append(filterFuncs, generatedFunc)
|
|
}
|
|
}
|
|
|
|
volumes, err := runtime.Volumes(getContext())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Get the volumes that match the filter
|
|
volsFiltered := make([]*adapter.Volume, 0, len(volumes))
|
|
for _, vol := range volumes {
|
|
include := true
|
|
for _, filter := range filterFuncs {
|
|
include = include && filter(vol)
|
|
}
|
|
|
|
if include {
|
|
volsFiltered = append(volsFiltered, vol)
|
|
}
|
|
}
|
|
return generateVolLsOutput(volsFiltered, opts)
|
|
}
|
|
|
|
// generate the template based on conditions given
|
|
func genVolLsFormat(c *cliconfig.VolumeLsValues) string {
|
|
var format string
|
|
if c.Format != "" {
|
|
// "\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"
|
|
format = strings.Replace(c.Format, `\t`, "\t", -1)
|
|
}
|
|
if c.Quiet {
|
|
format = "{{.Name}}"
|
|
}
|
|
return format
|
|
}
|
|
|
|
// Convert output to genericParams for printing
|
|
func volLsToGeneric(templParams []volumeLsTemplateParams, JSONParams []volumeLsJSONParams) (genericParams []interface{}) {
|
|
if len(templParams) > 0 {
|
|
for _, v := range templParams {
|
|
genericParams = append(genericParams, interface{}(v))
|
|
}
|
|
return
|
|
}
|
|
for _, v := range JSONParams {
|
|
genericParams = append(genericParams, interface{}(v))
|
|
}
|
|
return
|
|
}
|
|
|
|
// generate the accurate header based on template given
|
|
func (vol *volumeLsTemplateParams) volHeaderMap() map[string]string {
|
|
v := reflect.Indirect(reflect.ValueOf(vol))
|
|
values := make(map[string]string)
|
|
|
|
for i := 0; i < v.NumField(); i++ {
|
|
key := v.Type().Field(i).Name
|
|
value := key
|
|
if value == "Name" {
|
|
value = "Volume" + value
|
|
}
|
|
values[key] = strings.ToUpper(splitCamelCase(value))
|
|
}
|
|
return values
|
|
}
|
|
|
|
// getVolTemplateOutput returns all the volumes in the volumeLsTemplateParams format
|
|
func getVolTemplateOutput(lsParams []volumeLsJSONParams, opts volumeLsOptions) ([]volumeLsTemplateParams, error) {
|
|
var lsOutput []volumeLsTemplateParams
|
|
|
|
for _, lsParam := range lsParams {
|
|
var (
|
|
labels string
|
|
options string
|
|
)
|
|
|
|
for k, v := range lsParam.Labels {
|
|
label := k
|
|
if v != "" {
|
|
label += "=" + v
|
|
}
|
|
labels += label
|
|
}
|
|
for k, v := range lsParam.Options {
|
|
option := k
|
|
if v != "" {
|
|
option += "=" + v
|
|
}
|
|
options += option
|
|
}
|
|
params := volumeLsTemplateParams{
|
|
Name: lsParam.Name,
|
|
Driver: lsParam.Driver,
|
|
MountPoint: lsParam.MountPoint,
|
|
Scope: lsParam.Scope,
|
|
Labels: labels,
|
|
Options: options,
|
|
}
|
|
|
|
lsOutput = append(lsOutput, params)
|
|
}
|
|
return lsOutput, nil
|
|
}
|
|
|
|
// getVolJSONParams returns the volumes in JSON format
|
|
func getVolJSONParams(volumes []*adapter.Volume) []volumeLsJSONParams {
|
|
var lsOutput []volumeLsJSONParams
|
|
|
|
for _, volume := range volumes {
|
|
params := volumeLsJSONParams{
|
|
Name: volume.Name(),
|
|
Labels: volume.Labels(),
|
|
MountPoint: volume.MountPoint(),
|
|
Driver: volume.Driver(),
|
|
Options: volume.Options(),
|
|
Scope: volume.Scope(),
|
|
}
|
|
|
|
lsOutput = append(lsOutput, params)
|
|
}
|
|
return lsOutput
|
|
}
|
|
|
|
// generateVolLsOutput generates the output based on the format, JSON or Go Template, and prints it out
|
|
func generateVolLsOutput(volumes []*adapter.Volume, opts volumeLsOptions) error {
|
|
if len(volumes) == 0 && opts.Format != formats.JSONString {
|
|
return nil
|
|
}
|
|
lsOutput := getVolJSONParams(volumes)
|
|
var out formats.Writer
|
|
|
|
switch opts.Format {
|
|
case formats.JSONString:
|
|
out = formats.JSONStructArray{Output: volLsToGeneric([]volumeLsTemplateParams{}, lsOutput)}
|
|
default:
|
|
lsOutput, err := getVolTemplateOutput(lsOutput, opts)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to create volume output")
|
|
}
|
|
out = formats.StdoutTemplateArray{Output: volLsToGeneric(lsOutput, []volumeLsJSONParams{}), Template: opts.Format, Fields: lsOutput[0].volHeaderMap()}
|
|
}
|
|
return out.Out()
|
|
}
|
|
|
|
// generateVolumeFilterFuncs returns the true if the volume matches the filter set, otherwise it returns false.
|
|
func generateVolumeFilterFuncs(filter, filterValue string) (func(volume *adapter.Volume) bool, error) {
|
|
switch filter {
|
|
case "name":
|
|
return func(v *adapter.Volume) bool {
|
|
return strings.Contains(v.Name(), filterValue)
|
|
}, nil
|
|
case "driver":
|
|
return func(v *adapter.Volume) bool {
|
|
return v.Driver() == filterValue
|
|
}, nil
|
|
case "scope":
|
|
return func(v *adapter.Volume) bool {
|
|
return v.Scope() == filterValue
|
|
}, nil
|
|
case "label":
|
|
filterArray := strings.SplitN(filterValue, "=", 2)
|
|
filterKey := filterArray[0]
|
|
if len(filterArray) > 1 {
|
|
filterValue = filterArray[1]
|
|
} else {
|
|
filterValue = ""
|
|
}
|
|
return func(v *adapter.Volume) bool {
|
|
for labelKey, labelValue := range v.Labels() {
|
|
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}, nil
|
|
case "opt":
|
|
filterArray := strings.SplitN(filterValue, "=", 2)
|
|
filterKey := filterArray[0]
|
|
if len(filterArray) > 1 {
|
|
filterValue = filterArray[1]
|
|
} else {
|
|
filterValue = ""
|
|
}
|
|
return func(v *adapter.Volume) bool {
|
|
for labelKey, labelValue := range v.Options() {
|
|
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}, nil
|
|
}
|
|
return nil, errors.Errorf("%s is an invalid filter", filter)
|
|
}
|