github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/volume_ls.go (about) 1 package main 2 3 import ( 4 "reflect" 5 "strings" 6 7 "github.com/containers/buildah/pkg/formats" 8 "github.com/containers/libpod/cmd/podman/cliconfig" 9 "github.com/containers/libpod/pkg/adapter" 10 "github.com/pkg/errors" 11 "github.com/spf13/cobra" 12 ) 13 14 // volumeOptions is the "ls" command options 15 type volumeLsOptions struct { 16 Format string 17 Quiet bool 18 } 19 20 // volumeLsTemplateParams is the template parameters to list the volumes 21 type volumeLsTemplateParams struct { 22 Name string 23 Labels string 24 MountPoint string 25 Driver string 26 Options string 27 Scope string 28 } 29 30 // volumeLsJSONParams is the JSON parameters to list the volumes 31 type volumeLsJSONParams struct { 32 Name string `json:"name"` 33 Labels map[string]string `json:"labels"` 34 MountPoint string `json:"mountPoint"` 35 Driver string `json:"driver"` 36 Options map[string]string `json:"options"` 37 Scope string `json:"scope"` 38 } 39 40 var ( 41 volumeLsCommand cliconfig.VolumeLsValues 42 43 volumeLsDescription = ` 44 podman volume ls 45 46 List all available volumes. The output of the volumes can be filtered 47 and the output format can be changed to JSON or a user specified Go template.` 48 _volumeLsCommand = &cobra.Command{ 49 Use: "ls", 50 Aliases: []string{"list"}, 51 Args: noSubArgs, 52 Short: "List volumes", 53 Long: volumeLsDescription, 54 RunE: func(cmd *cobra.Command, args []string) error { 55 volumeLsCommand.InputArgs = args 56 volumeLsCommand.GlobalFlags = MainGlobalOpts 57 volumeLsCommand.Remote = remoteclient 58 return volumeLsCmd(&volumeLsCommand) 59 }, 60 } 61 ) 62 63 func init() { 64 volumeLsCommand.Command = _volumeLsCommand 65 volumeLsCommand.SetHelpTemplate(HelpTemplate()) 66 volumeLsCommand.SetUsageTemplate(UsageTemplate()) 67 flags := volumeLsCommand.Flags() 68 69 flags.StringVarP(&volumeLsCommand.Filter, "filter", "f", "", "Filter volume output") 70 flags.StringVar(&volumeLsCommand.Format, "format", "table {{.Driver}}\t{{.Name}}", "Format volume output using Go template") 71 flags.BoolVarP(&volumeLsCommand.Quiet, "quiet", "q", false, "Print volume output in quiet mode") 72 } 73 74 func volumeLsCmd(c *cliconfig.VolumeLsValues) error { 75 runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) 76 if err != nil { 77 return errors.Wrapf(err, "error creating libpod runtime") 78 } 79 defer runtime.DeferredShutdown(false) 80 81 opts := volumeLsOptions{ 82 Quiet: c.Quiet, 83 } 84 opts.Format = genVolLsFormat(c) 85 86 // Get the filter functions based on any filters set 87 var filterFuncs []adapter.VolumeFilter 88 if c.Filter != "" { 89 filters := strings.Split(c.Filter, ",") 90 for _, f := range filters { 91 filterSplit := strings.Split(f, "=") 92 if len(filterSplit) < 2 { 93 return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) 94 } 95 generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1]) 96 if err != nil { 97 return errors.Wrapf(err, "invalid filter") 98 } 99 filterFuncs = append(filterFuncs, generatedFunc) 100 } 101 } 102 103 volumes, err := runtime.Volumes(getContext()) 104 if err != nil { 105 return err 106 } 107 // Get the volumes that match the filter 108 volsFiltered := make([]*adapter.Volume, 0, len(volumes)) 109 for _, vol := range volumes { 110 include := true 111 for _, filter := range filterFuncs { 112 include = include && filter(vol) 113 } 114 115 if include { 116 volsFiltered = append(volsFiltered, vol) 117 } 118 } 119 return generateVolLsOutput(volsFiltered, opts) 120 } 121 122 // generate the template based on conditions given 123 func genVolLsFormat(c *cliconfig.VolumeLsValues) string { 124 var format string 125 if c.Format != "" { 126 // "\t" from the command line is not being recognized as a tab 127 // replacing the string "\t" to a tab character if the user passes in "\t" 128 format = strings.Replace(c.Format, `\t`, "\t", -1) 129 } 130 if c.Quiet { 131 format = "{{.Name}}" 132 } 133 return format 134 } 135 136 // Convert output to genericParams for printing 137 func volLsToGeneric(templParams []volumeLsTemplateParams, jsonParams []volumeLsJSONParams) (genericParams []interface{}) { 138 if len(templParams) > 0 { 139 for _, v := range templParams { 140 genericParams = append(genericParams, interface{}(v)) 141 } 142 return 143 } 144 for _, v := range jsonParams { 145 genericParams = append(genericParams, interface{}(v)) 146 } 147 return 148 } 149 150 // generate the accurate header based on template given 151 func (vol *volumeLsTemplateParams) volHeaderMap() map[string]string { 152 v := reflect.Indirect(reflect.ValueOf(vol)) 153 values := make(map[string]string) 154 155 for i := 0; i < v.NumField(); i++ { 156 key := v.Type().Field(i).Name 157 value := key 158 if value == "Name" { 159 value = "Volume" + value 160 } 161 values[key] = strings.ToUpper(splitCamelCase(value)) 162 } 163 return values 164 } 165 166 // getVolTemplateOutput returns all the volumes in the volumeLsTemplateParams format 167 func getVolTemplateOutput(lsParams []volumeLsJSONParams, opts volumeLsOptions) ([]volumeLsTemplateParams, error) { 168 var lsOutput []volumeLsTemplateParams 169 170 for _, lsParam := range lsParams { 171 var ( 172 labels string 173 options string 174 ) 175 176 for k, v := range lsParam.Labels { 177 label := k 178 if v != "" { 179 label += "=" + v 180 } 181 labels += label 182 } 183 for k, v := range lsParam.Options { 184 option := k 185 if v != "" { 186 option += "=" + v 187 } 188 options += option 189 } 190 params := volumeLsTemplateParams{ 191 Name: lsParam.Name, 192 Driver: lsParam.Driver, 193 MountPoint: lsParam.MountPoint, 194 Scope: lsParam.Scope, 195 Labels: labels, 196 Options: options, 197 } 198 199 lsOutput = append(lsOutput, params) 200 } 201 return lsOutput, nil 202 } 203 204 // getVolJSONParams returns the volumes in JSON format 205 func getVolJSONParams(volumes []*adapter.Volume) []volumeLsJSONParams { 206 var lsOutput []volumeLsJSONParams 207 208 for _, volume := range volumes { 209 params := volumeLsJSONParams{ 210 Name: volume.Name(), 211 Labels: volume.Labels(), 212 MountPoint: volume.MountPoint(), 213 Driver: volume.Driver(), 214 Options: volume.Options(), 215 Scope: volume.Scope(), 216 } 217 218 lsOutput = append(lsOutput, params) 219 } 220 return lsOutput 221 } 222 223 // generateVolLsOutput generates the output based on the format, JSON or Go Template, and prints it out 224 func generateVolLsOutput(volumes []*adapter.Volume, opts volumeLsOptions) error { 225 if len(volumes) == 0 && opts.Format != formats.JSONString { 226 return nil 227 } 228 lsOutput := getVolJSONParams(volumes) 229 var out formats.Writer 230 231 switch opts.Format { 232 case formats.JSONString: 233 out = formats.JSONStructArray{Output: volLsToGeneric([]volumeLsTemplateParams{}, lsOutput)} 234 default: 235 lsOutput, err := getVolTemplateOutput(lsOutput, opts) 236 if err != nil { 237 return errors.Wrapf(err, "unable to create volume output") 238 } 239 out = formats.StdoutTemplateArray{Output: volLsToGeneric(lsOutput, []volumeLsJSONParams{}), Template: opts.Format, Fields: lsOutput[0].volHeaderMap()} 240 } 241 return out.Out() 242 } 243 244 // generateVolumeFilterFuncs returns the true if the volume matches the filter set, otherwise it returns false. 245 func generateVolumeFilterFuncs(filter, filterValue string) (func(volume *adapter.Volume) bool, error) { 246 switch filter { 247 case "name": 248 return func(v *adapter.Volume) bool { 249 return strings.Contains(v.Name(), filterValue) 250 }, nil 251 case "driver": 252 return func(v *adapter.Volume) bool { 253 return v.Driver() == filterValue 254 }, nil 255 case "scope": 256 return func(v *adapter.Volume) bool { 257 return v.Scope() == filterValue 258 }, nil 259 case "label": 260 filterArray := strings.SplitN(filterValue, "=", 2) 261 filterKey := filterArray[0] 262 if len(filterArray) > 1 { 263 filterValue = filterArray[1] 264 } else { 265 filterValue = "" 266 } 267 return func(v *adapter.Volume) bool { 268 for labelKey, labelValue := range v.Labels() { 269 if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { 270 return true 271 } 272 } 273 return false 274 }, nil 275 case "opt": 276 filterArray := strings.SplitN(filterValue, "=", 2) 277 filterKey := filterArray[0] 278 if len(filterArray) > 1 { 279 filterValue = filterArray[1] 280 } else { 281 filterValue = "" 282 } 283 return func(v *adapter.Volume) bool { 284 for labelKey, labelValue := range v.Options() { 285 if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { 286 return true 287 } 288 } 289 return false 290 }, nil 291 } 292 return nil, errors.Errorf("%s is an invalid filter", filter) 293 }