mirror of https://github.com/containers/podman.git
Merge pull request #8689 from bblenard/issue-8672-volume-prune
Add volume prune --filter support
This commit is contained in:
commit
e5741b984e
|
@ -8,10 +8,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/common/pkg/completion"
|
"github.com/containers/common/pkg/completion"
|
||||||
|
"github.com/containers/podman/v2/cmd/podman/common"
|
||||||
"github.com/containers/podman/v2/cmd/podman/registry"
|
"github.com/containers/podman/v2/cmd/podman/registry"
|
||||||
"github.com/containers/podman/v2/cmd/podman/utils"
|
"github.com/containers/podman/v2/cmd/podman/utils"
|
||||||
"github.com/containers/podman/v2/cmd/podman/validate"
|
"github.com/containers/podman/v2/cmd/podman/validate"
|
||||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||||
|
"github.com/containers/podman/v2/pkg/domain/filters"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,6 +30,7 @@ var (
|
||||||
RunE: prune,
|
RunE: prune,
|
||||||
ValidArgsFunction: completion.AutocompleteNone,
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
}
|
}
|
||||||
|
filter = []string{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -37,10 +40,17 @@ func init() {
|
||||||
Parent: volumeCmd,
|
Parent: volumeCmd,
|
||||||
})
|
})
|
||||||
flags := pruneCommand.Flags()
|
flags := pruneCommand.Flags()
|
||||||
|
|
||||||
|
filterFlagName := "filter"
|
||||||
|
flags.StringArrayVar(&filter, filterFlagName, []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
|
||||||
|
_ = pruneCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteVolumeFilters)
|
||||||
flags.BoolP("force", "f", false, "Do not prompt for confirmation")
|
flags.BoolP("force", "f", false, "Do not prompt for confirmation")
|
||||||
}
|
}
|
||||||
|
|
||||||
func prune(cmd *cobra.Command, args []string) error {
|
func prune(cmd *cobra.Command, args []string) error {
|
||||||
|
var (
|
||||||
|
pruneOptions = entities.VolumePruneOptions{}
|
||||||
|
)
|
||||||
// Prompt for confirmation if --force is not set
|
// Prompt for confirmation if --force is not set
|
||||||
force, err := cmd.Flags().GetBool("force")
|
force, err := cmd.Flags().GetBool("force")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -58,7 +68,11 @@ func prune(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responses, err := registry.ContainerEngine().VolumePrune(context.Background())
|
pruneOptions.Filters, err = filters.ParseFilterArgumentsIntoFilters(filter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
responses, err := registry.ContainerEngine().VolumePrune(context.Background(), pruneOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ podman\-volume\-prune - Remove all unused volumes
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
|
|
||||||
Removes all unused volumes. You will be prompted to confirm the removal of all the
|
Removes unused volumes. By default all unused volumes will be removed, the **--filter** flag can
|
||||||
|
be used to filter specific volumes. You will be prompted to confirm the removal of all the
|
||||||
unused volumes. To bypass the confirmation, use the **--force** flag.
|
unused volumes. To bypass the confirmation, use the **--force** flag.
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,6 +19,17 @@ unused volumes. To bypass the confirmation, use the **--force** flag.
|
||||||
|
|
||||||
Do not prompt for confirmation.
|
Do not prompt for confirmation.
|
||||||
|
|
||||||
|
#### **--filter**
|
||||||
|
|
||||||
|
Filter volumes to be pruned. Volumes can be filtered by the following attributes:
|
||||||
|
|
||||||
|
- dangling
|
||||||
|
- driver
|
||||||
|
- label
|
||||||
|
- name
|
||||||
|
- opt
|
||||||
|
- scope
|
||||||
|
|
||||||
#### **--help**
|
#### **--help**
|
||||||
|
|
||||||
Print usage statement
|
Print usage statement
|
||||||
|
@ -29,6 +41,8 @@ Print usage statement
|
||||||
$ podman volume prune
|
$ podman volume prune
|
||||||
|
|
||||||
$ podman volume prune --force
|
$ podman volume prune --force
|
||||||
|
|
||||||
|
$ podman volume prune --filter label=mylabel=mylabelvalue
|
||||||
```
|
```
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
|
|
|
@ -133,9 +133,9 @@ func (r *Runtime) GetAllVolumes() ([]*Volume, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PruneVolumes removes unused volumes from the system
|
// PruneVolumes removes unused volumes from the system
|
||||||
func (r *Runtime) PruneVolumes(ctx context.Context) (map[string]error, error) {
|
func (r *Runtime) PruneVolumes(ctx context.Context, filterFuncs []VolumeFilter) (map[string]error, error) {
|
||||||
reports := make(map[string]error)
|
reports := make(map[string]error)
|
||||||
vols, err := r.GetAllVolumes()
|
vols, err := r.Volumes(filterFuncs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package compat
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/podman/v2/libpod"
|
"github.com/containers/podman/v2/libpod"
|
||||||
|
@ -254,14 +255,15 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) {
|
||||||
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO: We have no ability to pass pruning filters to `PruneVolumes()` so
|
|
||||||
// we'll explicitly reject the request if we see any
|
f := (url.Values)(query.Filters)
|
||||||
if len(query.Filters) > 0 {
|
filterFuncs, err := filters.GenerateVolumeFilters(f)
|
||||||
utils.InternalServerError(w, errors.New("filters for pruning volumes is not implemented"))
|
if err != nil {
|
||||||
|
utils.Error(w, "Something when wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse filters for %s", f.Encode()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pruned, err := runtime.PruneVolumes(r.Context())
|
pruned, err := runtime.PruneVolumes(r.Context(), filterFuncs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.InternalServerError(w, err)
|
utils.InternalServerError(w, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -3,6 +3,7 @@ package libpod
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/containers/podman/v2/libpod"
|
"github.com/containers/podman/v2/libpod"
|
||||||
"github.com/containers/podman/v2/libpod/define"
|
"github.com/containers/podman/v2/libpod/define"
|
||||||
|
@ -180,8 +181,25 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) {
|
||||||
func pruneVolumesHelper(r *http.Request) ([]*entities.VolumePruneReport, error) {
|
func pruneVolumesHelper(r *http.Request) ([]*entities.VolumePruneReport, error) {
|
||||||
var (
|
var (
|
||||||
runtime = r.Context().Value("runtime").(*libpod.Runtime)
|
runtime = r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
|
decoder = r.Context().Value("decoder").(*schema.Decoder)
|
||||||
)
|
)
|
||||||
pruned, err := runtime.PruneVolumes(r.Context())
|
query := struct {
|
||||||
|
Filters map[string][]string `schema:"filters"`
|
||||||
|
}{
|
||||||
|
// override any golang type defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f := (url.Values)(query.Filters)
|
||||||
|
filterFuncs, err := filters.GenerateVolumeFilters(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pruned, err := runtime.PruneVolumes(r.Context(), filterFuncs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,16 +144,15 @@ var _ = Describe("Podman volumes", func() {
|
||||||
Expect(vols[0].Name).To(Equal("homer"))
|
Expect(vols[0].Name).To(Equal("homer"))
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO we need to add filtering to tests
|
|
||||||
It("prune unused volume", func() {
|
It("prune unused volume", func() {
|
||||||
// Pruning when no volumes present should be ok
|
// Pruning when no volumes present should be ok
|
||||||
_, err := volumes.Prune(connText)
|
_, err := volumes.Prune(connText, nil)
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
|
|
||||||
// Removing an unused volume should work
|
// Removing an unused volume should work
|
||||||
_, err = volumes.Create(connText, entities.VolumeCreateOptions{})
|
_, err = volumes.Create(connText, entities.VolumeCreateOptions{})
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
vols, err := volumes.Prune(connText)
|
vols, err := volumes.Prune(connText, nil)
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
Expect(len(vols)).To(BeNumerically("==", 1))
|
Expect(len(vols)).To(BeNumerically("==", 1))
|
||||||
|
|
||||||
|
@ -163,11 +162,45 @@ var _ = Describe("Podman volumes", func() {
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/homer", "homer"), "--name", "vtest", alpine.name, "top"})
|
session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/homer", "homer"), "--name", "vtest", alpine.name, "top"})
|
||||||
session.Wait(45)
|
session.Wait(45)
|
||||||
vols, err = volumes.Prune(connText)
|
vols, err = volumes.Prune(connText, nil)
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
Expect(len(vols)).To(BeNumerically("==", 1))
|
Expect(len(vols)).To(BeNumerically("==", 1))
|
||||||
_, err = volumes.Inspect(connText, "homer")
|
_, err = volumes.Inspect(connText, "homer")
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
|
|
||||||
|
// Removing volume with non matching filter shouldn't prune any volumes
|
||||||
|
filters := make(map[string][]string)
|
||||||
|
filters["label"] = []string{"label1=idontmatch"}
|
||||||
|
_, err = volumes.Create(connText, entities.VolumeCreateOptions{Label: map[string]string{
|
||||||
|
"label1": "value1",
|
||||||
|
}})
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
vols, err = volumes.Prune(connText, filters)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(len(vols)).To(BeNumerically("==", 0))
|
||||||
|
vol2, err := volumes.Create(connText, entities.VolumeCreateOptions{Label: map[string]string{
|
||||||
|
"label1": "value2",
|
||||||
|
}})
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
_, err = volumes.Create(connText, entities.VolumeCreateOptions{Label: map[string]string{
|
||||||
|
"label1": "value3",
|
||||||
|
}})
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
|
||||||
|
// Removing volume with matching filter label and value should remove specific entry
|
||||||
|
filters = make(map[string][]string)
|
||||||
|
filters["label"] = []string{"label1=value2"}
|
||||||
|
vols, err = volumes.Prune(connText, filters)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(len(vols)).To(BeNumerically("==", 1))
|
||||||
|
Expect(vols[0].Id).To(Equal(vol2.Name))
|
||||||
|
|
||||||
|
// Removing volumes with matching filter label should remove all matching volumes
|
||||||
|
filters = make(map[string][]string)
|
||||||
|
filters["label"] = []string{"label1"}
|
||||||
|
vols, err = volumes.Prune(connText, filters)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(len(vols)).To(BeNumerically("==", 2))
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -75,7 +75,7 @@ func List(ctx context.Context, filters map[string][]string) ([]*entities.VolumeL
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune removes unused volumes from the local filesystem.
|
// Prune removes unused volumes from the local filesystem.
|
||||||
func Prune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
|
func Prune(ctx context.Context, filters map[string][]string) ([]*entities.VolumePruneReport, error) {
|
||||||
var (
|
var (
|
||||||
pruned []*entities.VolumePruneReport
|
pruned []*entities.VolumePruneReport
|
||||||
)
|
)
|
||||||
|
@ -83,7 +83,15 @@ func Prune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", nil, nil)
|
params := url.Values{}
|
||||||
|
if len(filters) > 0 {
|
||||||
|
strFilters, err := bindings.FiltersToString(filters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
params.Set("filters", strFilters)
|
||||||
|
}
|
||||||
|
response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", params, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,6 @@ type ContainerEngine interface {
|
||||||
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
|
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
|
||||||
VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error)
|
VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error)
|
||||||
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
|
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
|
||||||
VolumePrune(ctx context.Context) ([]*VolumePruneReport, error)
|
VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*VolumePruneReport, error)
|
||||||
VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
|
VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package entities
|
package entities
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
docker_api_types "github.com/docker/docker/api/types"
|
docker_api_types "github.com/docker/docker/api/types"
|
||||||
|
@ -109,6 +110,12 @@ type VolumeInspectReport struct {
|
||||||
*VolumeConfigResponse
|
*VolumeConfigResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VolumePruneOptions describes the options needed
|
||||||
|
// to prune a volume from the CLI
|
||||||
|
type VolumePruneOptions struct {
|
||||||
|
Filters url.Values `json:"filters" schema:"filters"`
|
||||||
|
}
|
||||||
|
|
||||||
type VolumePruneReport struct {
|
type VolumePruneReport struct {
|
||||||
Err error
|
Err error
|
||||||
Id string //nolint
|
Id string //nolint
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package filters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseFilterArgumentsIntoFilters(filters []string) (url.Values, error) {
|
||||||
|
parsedFilters := make(url.Values)
|
||||||
|
for _, f := range filters {
|
||||||
|
t := strings.SplitN(f, "=", 2)
|
||||||
|
if len(t) < 2 {
|
||||||
|
return parsedFilters, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
|
||||||
|
}
|
||||||
|
parsedFilters.Add(t[0], t[1])
|
||||||
|
}
|
||||||
|
return parsedFilters, nil
|
||||||
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
package filters
|
package filters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/podman/v2/libpod"
|
"github.com/containers/podman/v2/libpod"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateVolumeFilters(filters map[string][]string) ([]libpod.VolumeFilter, error) {
|
func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) {
|
||||||
var vf []libpod.VolumeFilter
|
var vf []libpod.VolumeFilter
|
||||||
for filter, v := range filters {
|
for filter, v := range filters {
|
||||||
for _, val := range v {
|
for _, val := range v {
|
||||||
|
|
|
@ -224,7 +224,7 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys
|
||||||
systemPruneReport.ImagePruneReport.Report.Id = append(systemPruneReport.ImagePruneReport.Report.Id, results...)
|
systemPruneReport.ImagePruneReport.Report.Id = append(systemPruneReport.ImagePruneReport.Report.Id, results...)
|
||||||
}
|
}
|
||||||
if options.Volume {
|
if options.Volume {
|
||||||
volumePruneReport, err := ic.pruneVolumesHelper(ctx)
|
volumePruneReport, err := ic.pruneVolumesHelper(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,12 +127,16 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
|
||||||
return reports, errs, nil
|
return reports, errs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
|
func (ic *ContainerEngine) VolumePrune(ctx context.Context, options entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
|
||||||
return ic.pruneVolumesHelper(ctx)
|
filterFuncs, err := filters.GenerateVolumeFilters(options.Filters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ic.pruneVolumesHelper(ctx, filterFuncs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context) ([]*entities.VolumePruneReport, error) {
|
func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context, filterFuncs []libpod.VolumeFilter) ([]*entities.VolumePruneReport, error) {
|
||||||
pruned, err := ic.Libpod.PruneVolumes(ctx)
|
pruned, err := ic.Libpod.PruneVolumes(ctx, filterFuncs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,8 +68,8 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
|
||||||
return reports, errs, nil
|
return reports, errs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
|
func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
|
||||||
return volumes.Prune(ic.ClientCxt)
|
return volumes.Prune(ic.ClientCxt, (map[string][]string)(opts.Filters))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) {
|
func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) {
|
||||||
|
|
|
@ -20,6 +20,18 @@ t POST libpod/volumes/create \
|
||||||
.Labels.testlabel=testonly \
|
.Labels.testlabel=testonly \
|
||||||
.Options.type=tmpfs \
|
.Options.type=tmpfs \
|
||||||
.Options.o=nodev,noexec
|
.Options.o=nodev,noexec
|
||||||
|
t POST libpod/volumes/create \
|
||||||
|
'"Name":"foo3","Label":{"testlabel":""},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \
|
||||||
|
.Name=foo3 \
|
||||||
|
.Labels.testlabel="" \
|
||||||
|
.Options.type=tmpfs \
|
||||||
|
.Options.o=nodev,noexec
|
||||||
|
t POST libpod/volumes/create \
|
||||||
|
'"Name":"foo4","Label":{"testlabel1":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \
|
||||||
|
.Name=foo4 \
|
||||||
|
.Labels.testlabel1=testonly \
|
||||||
|
.Options.type=tmpfs \
|
||||||
|
.Options.o=nodev,noexec
|
||||||
|
|
||||||
# Negative test
|
# Negative test
|
||||||
# We have created a volume named "foo1"
|
# We have created a volume named "foo1"
|
||||||
|
@ -39,6 +51,12 @@ t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22foo1%22%5D%7D 200 length
|
||||||
t GET libpod/volumes/json?filters=%7B%22name%22%3A%20%5B%22foo1%22%2C%20%22foo2%22%5D%7D 200 length=2 .[0].Name=foo1 .[1].Name=foo2
|
t GET libpod/volumes/json?filters=%7B%22name%22%3A%20%5B%22foo1%22%2C%20%22foo2%22%5D%7D 200 length=2 .[0].Name=foo1 .[1].Name=foo2
|
||||||
# -G --data-urlencode 'filters={"name":["notexist"]}'
|
# -G --data-urlencode 'filters={"name":["notexist"]}'
|
||||||
t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22notexists%22%5D%7D 200 length=0
|
t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22notexists%22%5D%7D 200 length=0
|
||||||
|
# -G --data-urlencode 'filters={"label":["testlabel"]}'
|
||||||
|
t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel%22%5D%7D 200 length=2
|
||||||
|
# -G --data-urlencode 'filters={"label":["testlabel=testonly"]}'
|
||||||
|
t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel=testonly%22%5D%7D 200 length=1
|
||||||
|
# -G --data-urlencode 'filters={"label":["testlabel1=testonly"]}'
|
||||||
|
t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D 200 length=1
|
||||||
|
|
||||||
## inspect volume
|
## inspect volume
|
||||||
t GET libpod/volumes/foo1/json 200 \
|
t GET libpod/volumes/foo1/json 200 \
|
||||||
|
@ -60,6 +78,18 @@ t DELETE libpod/volumes/foo1 404 \
|
||||||
.message~.* \
|
.message~.* \
|
||||||
.response=404
|
.response=404
|
||||||
|
|
||||||
|
## Prune volumes with label matching 'testlabel1=testonly'
|
||||||
|
# -G --data-urlencode 'filters={"label":["testlabel1=testonly"]}'
|
||||||
|
t POST libpod/volumes/prune?filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D "" 200
|
||||||
|
# -G --data-urlencode 'filters={"label":["testlabel1=testonly"]}'
|
||||||
|
t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D 200 length=0
|
||||||
|
|
||||||
|
## Prune volumes with label matching 'testlabel'
|
||||||
|
# -G --data-urlencode 'filters={"label":["testlabel"]}'
|
||||||
|
t POST libpod/volumes/prune?filters=%7B%22label%22:%5B%22testlabel%22%5D%7D "" 200
|
||||||
|
# -G --data-urlencode 'filters={"label":["testlabel"]}'
|
||||||
|
t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel%22%5D%7D 200 length=0
|
||||||
|
|
||||||
## Prune volumes
|
## Prune volumes
|
||||||
t POST libpod/volumes/prune "" 200
|
t POST libpod/volumes/prune "" 200
|
||||||
#After prune volumes, there should be no volume existing
|
#After prune volumes, there should be no volume existing
|
||||||
|
|
|
@ -62,6 +62,66 @@ var _ = Describe("Podman volume prune", func() {
|
||||||
podmanTest.Cleanup()
|
podmanTest.Cleanup()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman prune volume --filter", func() {
|
||||||
|
session := podmanTest.Podman([]string{"volume", "create", "--label", "label1=value1", "myvol1"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv1", "myvol2"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv2", "myvol3"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1", "myvol4"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"create", "-v", "myvol5:/myvol5", ALPINE, "ls"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"create", "-v", "myvol6:/myvol6", ALPINE, "ls"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "ls"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
Expect(len(session.OutputToStringArray())).To(Equal(7))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "label=label1=value1"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "ls"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
Expect(len(session.OutputToStringArray())).To(Equal(6))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "label=sharedlabel1=slv1"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "ls"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
Expect(len(session.OutputToStringArray())).To(Equal(5))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "label=sharedlabel1"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"volume", "ls"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
Expect(len(session.OutputToStringArray())).To(Equal(3))
|
||||||
|
|
||||||
|
podmanTest.Cleanup()
|
||||||
|
})
|
||||||
|
|
||||||
It("podman system prune --volume", func() {
|
It("podman system prune --volume", func() {
|
||||||
session := podmanTest.Podman([]string{"volume", "create"})
|
session := podmanTest.Podman([]string{"volume", "create"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
|
|
Loading…
Reference in New Issue