aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2021-03-18 06:32:29 -0700
committerGitHub <noreply@github.com>2021-03-18 06:32:29 -0700
commit629183bd7f0073dfcbfa4d611abc62a9c5711dab (patch)
tree812ebb2c5a6685f1f333ec960365e97b4e91457f
parent77b3a2df645f2548f7bd2da85bbdb17e4de98310 (diff)
parent8ea02d0b6033b6ffdc68d38f3276410f4e2e8eb9 (diff)
downloadpodman-629183bd7f0073dfcbfa4d611abc62a9c5711dab.tar.gz
podman-629183bd7f0073dfcbfa4d611abc62a9c5711dab.tar.bz2
podman-629183bd7f0073dfcbfa4d611abc62a9c5711dab.zip
Merge pull request #9710 from jmguzik/network-prune-filters-http-api
Network prune filters for http api (compat and libpod)
-rw-r--r--libpod/network/netconflist.go84
-rw-r--r--pkg/api/handlers/compat/networks.go18
-rw-r--r--pkg/api/handlers/libpod/networks.go17
-rw-r--r--pkg/api/server/register_networks.go1
-rw-r--r--pkg/domain/entities/network.go4
-rw-r--r--pkg/domain/filters/containers.go11
-rw-r--r--pkg/domain/infra/abi/network.go26
-rw-r--r--pkg/util/filters.go25
-rw-r--r--test/apiv2/35-networks.at28
9 files changed, 176 insertions, 38 deletions
diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go
index a45a4109a..b358bc530 100644
--- a/libpod/network/netconflist.go
+++ b/libpod/network/netconflist.go
@@ -5,8 +5,11 @@ import (
"os"
"path/filepath"
"strings"
+ "syscall"
+ "time"
"github.com/containernetworking/cni/libcni"
+ "github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/pkg/network"
"github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
@@ -222,24 +225,7 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri
case "label":
// matches all labels
- labels := GetNetworkLabels(netconf)
- outer:
- for _, filterValue := range filterValues {
- filterArray := strings.SplitN(filterValue, "=", 2)
- filterKey := filterArray[0]
- if len(filterArray) > 1 {
- filterValue = filterArray[1]
- } else {
- filterValue = ""
- }
- for labelKey, labelValue := range labels {
- if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
- result = true
- continue outer
- }
- }
- result = false
- }
+ result = matchPruneLabelFilters(netconf, filterValues)
case "driver":
// matches only for the DefaultNetworkDriver
@@ -268,3 +254,65 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri
}
return result, nil
}
+
+// IfPassesPruneFilter filters NetworkListReport and returns true if the prune filter match the given config
+func IfPassesPruneFilter(config *config.Config, netconf *libcni.NetworkConfigList, f map[string][]string) (bool, error) {
+ for key, filterValues := range f {
+ switch strings.ToLower(key) {
+ case "label":
+ return matchPruneLabelFilters(netconf, filterValues), nil
+ case "until":
+ until, err := util.ComputeUntilTimestamp(key, filterValues)
+ if err != nil {
+ return false, err
+ }
+ created, err := getCreatedTimestamp(config, netconf)
+ if err != nil {
+ return false, err
+ }
+ if created.Before(until) {
+ return true, nil
+ }
+ default:
+ return false, errors.Errorf("invalid filter %q", key)
+ }
+ }
+ return false, nil
+}
+
+func matchPruneLabelFilters(netconf *libcni.NetworkConfigList, filterValues []string) bool {
+ labels := GetNetworkLabels(netconf)
+ result := true
+outer:
+ for _, filterValue := range filterValues {
+ filterArray := strings.SplitN(filterValue, "=", 2)
+ filterKey := filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ for labelKey, labelValue := range labels {
+ if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
+ result = true
+ continue outer
+ }
+ }
+ result = false
+ }
+ return result
+}
+
+func getCreatedTimestamp(config *config.Config, netconf *libcni.NetworkConfigList) (*time.Time, error) {
+ networkConfigPath, err := GetCNIConfigPathByNameOrID(config, netconf.Name)
+ if err != nil {
+ return nil, err
+ }
+ f, err := os.Stat(networkConfigPath)
+ if err != nil {
+ return nil, err
+ }
+ stat := f.Sys().(*syscall.Stat_t)
+ created := time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) // nolint: unconvert
+ return &created, nil
+}
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go
index dfb1d7fda..7e06cad66 100644
--- a/pkg/api/handlers/compat/networks.go
+++ b/pkg/api/handlers/compat/networks.go
@@ -400,10 +400,24 @@ func Disconnect(w http.ResponseWriter, r *http.Request) {
// Prune removes unused networks
func Prune(w http.ResponseWriter, r *http.Request) {
- // TODO Filters are not implemented
runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ filters, err := filtersFromRequest(r)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+ filterMap := map[string][]string{}
+ for _, filter := range filters {
+ split := strings.SplitN(filter, "=", 2)
+ if len(split) > 1 {
+ filterMap[split[0]] = append(filterMap[split[0]], split[1])
+ }
+ }
+
ic := abi.ContainerEngine{Libpod: runtime}
- pruneOptions := entities.NetworkPruneOptions{}
+ pruneOptions := entities.NetworkPruneOptions{
+ Filters: filterMap,
+ }
pruneReports, err := ic.NetworkPrune(r.Context(), pruneOptions)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index 48cd37994..19c9ed658 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -177,10 +177,23 @@ func ExistsNetwork(w http.ResponseWriter, r *http.Request) {
// Prune removes unused networks
func Prune(w http.ResponseWriter, r *http.Request) {
- // TODO Filters are not implemented
runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Filters map[string][]string `schema:"filters"`
+ }{
+ // override any golang type defaults
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+
+ pruneOptions := entities.NetworkPruneOptions{
+ Filters: query.Filters,
+ }
ic := abi.ContainerEngine{Libpod: runtime}
- pruneOptions := entities.NetworkPruneOptions{}
pruneReports, err := ic.NetworkPrune(r.Context(), pruneOptions)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go
index c54de952f..a07c6a55e 100644
--- a/pkg/api/server/register_networks.go
+++ b/pkg/api/server/register_networks.go
@@ -172,7 +172,6 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// name: filters
// type: string
// description: |
- // NOT IMPLEMENTED
// Filters to process on the prune list, encoded as JSON (a map[string][]string).
// Available filters:
// - until=<timestamp> Prune networks created before this timestamp. The <timestamp> can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. 10m, 1h30m) computed relative to the daemon machine’s time.
diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go
index f66a7f575..a89501664 100644
--- a/pkg/domain/entities/network.go
+++ b/pkg/domain/entities/network.go
@@ -92,4 +92,6 @@ type NetworkPruneReport struct {
// NetworkPruneOptions describes options for pruning
// unused cni networks
-type NetworkPruneOptions struct{}
+type NetworkPruneOptions struct {
+ Filters map[string][]string
+}
diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go
index 98b8f7e88..02727e841 100644
--- a/pkg/domain/filters/containers.go
+++ b/pkg/domain/filters/containers.go
@@ -8,7 +8,6 @@ import (
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/network"
- "github.com/containers/podman/v3/pkg/timetype"
"github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
)
@@ -186,18 +185,10 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
return false
}, nil
case "until":
- if len(filterValues) != 1 {
- return nil, errors.Errorf("specify exactly one timestamp for %s", filter)
- }
- ts, err := timetype.GetTimestamp(filterValues[0], time.Now())
- if err != nil {
- return nil, err
- }
- seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
+ until, err := util.ComputeUntilTimestamp(filter, filterValues)
if err != nil {
return nil, err
}
- until := time.Unix(seconds, nanoseconds)
return func(c *libpod.Container) bool {
if !until.IsZero() && c.CreatedTime().After((until)) {
return true
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
index edde8ece6..1a833332c 100644
--- a/pkg/domain/infra/abi/network.go
+++ b/pkg/domain/infra/abi/network.go
@@ -174,17 +174,37 @@ func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.Ne
if err != nil {
return nil, err
}
+ networks, err := network.LoadCNIConfsFromDir(network.GetCNIConfDir(runtimeConfig))
+ if err != nil {
+ return nil, err
+ }
+
// Gather up all the non-default networks that the
// containers want
- usedNetworks := make(map[string]bool)
+ networksToKeep := make(map[string]bool)
for _, c := range cons {
nets, _, err := c.Networks()
if err != nil {
return nil, err
}
for _, n := range nets {
- usedNetworks[n] = true
+ networksToKeep[n] = true
+ }
+ }
+ if len(options.Filters) != 0 {
+ for _, n := range networks {
+ // This network will be kept anyway
+ if _, found := networksToKeep[n.Name]; found {
+ continue
+ }
+ ok, err := network.IfPassesPruneFilter(runtimeConfig, n, options.Filters)
+ if err != nil {
+ return nil, err
+ }
+ if !ok {
+ networksToKeep[n.Name] = true
+ }
}
}
- return network.PruneNetworks(runtimeConfig, usedNetworks)
+ return network.PruneNetworks(runtimeConfig, networksToKeep)
}
diff --git a/pkg/util/filters.go b/pkg/util/filters.go
new file mode 100644
index 000000000..bf16f89e3
--- /dev/null
+++ b/pkg/util/filters.go
@@ -0,0 +1,25 @@
+package util
+
+import (
+ "time"
+
+ "github.com/containers/podman/v3/pkg/timetype"
+ "github.com/pkg/errors"
+)
+
+// ComputeUntilTimestamp extracts unitil timestamp from filters
+func ComputeUntilTimestamp(filter string, filterValues []string) (time.Time, error) {
+ invalid := time.Time{}
+ if len(filterValues) != 1 {
+ return invalid, errors.Errorf("specify exactly one timestamp for %s", filter)
+ }
+ ts, err := timetype.GetTimestamp(filterValues[0], time.Now())
+ if err != nil {
+ return invalid, err
+ }
+ seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
+ if err != nil {
+ return invalid, err
+ }
+ return time.Unix(seconds, nanoseconds), nil
+}
diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at
index ce7ca628a..6c3a34ece 100644
--- a/test/apiv2/35-networks.at
+++ b/test/apiv2/35-networks.at
@@ -16,6 +16,20 @@ t POST libpod/networks/create?name=network2 \
200 \
.Filename~.*/network2\\.conflist
+# --data '{"Subnet":{"IP":"10.10.133.0","Mask":[255,255,255,0]},"Labels":{"xyz":"val"}}'
+t POST libpod/networks/create?name=network3 \
+ Subnet='{"IP":"10.10.133.0","Mask":[255,255,255,0]}' \
+ Labels='{"xyz":"val"}' \
+ 200 \
+ .Filename~.*/network3\\.conflist
+
+# --data '{"Subnet":{"IP":"10.10.134.0","Mask":[255,255,255,0]},"Labels":{"zaq":"val"}}'
+t POST libpod/networks/create?name=network4 \
+ Subnet='{"IP":"10.10.134.0","Mask":[255,255,255,0]}' \
+ Labels='{"zaq":"val"}' \
+ 200 \
+ .Filename~.*/network4\\.conflist
+
# test for empty mask
t POST libpod/networks/create Subnet='{"IP":"10.10.1.0","Mask":[]}' 500 \
.cause~'.*cannot be empty'
@@ -38,7 +52,7 @@ t GET libpod/networks/network1/json 200 \
t GET networks?filters='{"name":["network1","network2"]}' 200 \
length=2
t GET networks?filters='{"name":["network"]}' 200 \
- length=2
+ length=4
t GET networks?filters='{"label":["abc"]}' 200 \
length=1
# old docker filter type see #9526
@@ -66,6 +80,18 @@ t POST networks/create Name=net3\ IPAM='{"Config":[]}' 201
# network delete docker
t DELETE networks/net3 204
+# Prune networks compat api - bad filter input
+t POST networks/prune?filters='garb1age}' 500 \
+ .cause="invalid character 'g' looking for beginning of value"
+
+# prune networks using filter - compat api
+t POST networks/prune?filters='{"label":["xyz"]}' 200
+t GET networks/json?filters='{"label":["xyz"]}' 404
+
+# prune networks using filter - libpod api
+t POST libpod/networks/prune?filters='{"label":["zaq=val"]}' 200
+t GET libpod/networks/json?filters='{"label":["zaq=val"]}' 200 length=0
+
# clean the network
t DELETE libpod/networks/network1 200 \
.[0].Name~network1 \