summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2021-02-08 09:50:50 -0500
committerGitHub <noreply@github.com>2021-02-08 09:50:50 -0500
commitc32913d0a34def9fd3775ccf7dcef631942ee2b9 (patch)
tree8c624518b9b9b5e97bbc2a3fedba901660684a86
parent2aaf631586e82192e6b7b992e6b5c8717eb792d7 (diff)
parent91ea3fabd625a891487cd0d9b130ac71366ecb74 (diff)
downloadpodman-c32913d0a34def9fd3775ccf7dcef631942ee2b9.tar.gz
podman-c32913d0a34def9fd3775ccf7dcef631942ee2b9.tar.bz2
podman-c32913d0a34def9fd3775ccf7dcef631942ee2b9.zip
Merge pull request #9236 from baude/networkprune
add network prune
-rw-r--r--cmd/podman/networks/prune.go82
-rw-r--r--docs/source/markdown/podman-network-prune.1.md31
-rw-r--r--docs/source/markdown/podman-network.1.md1
-rw-r--r--docs/source/network.rst2
-rw-r--r--libpod/network/network.go50
-rw-r--r--pkg/api/handlers/compat/networks.go22
-rw-r--r--pkg/api/handlers/compat/swagger.go7
-rw-r--r--pkg/api/handlers/libpod/networks.go14
-rw-r--r--pkg/api/server/register_networks.go66
-rw-r--r--pkg/bindings/network/network.go18
-rw-r--r--pkg/bindings/network/types.go6
-rw-r--r--pkg/bindings/network/types_prune_options.go75
-rw-r--r--pkg/domain/entities/engine_container.go1
-rw-r--r--pkg/domain/entities/network.go12
-rw-r--r--pkg/domain/infra/abi/network.go25
-rw-r--r--pkg/domain/infra/tunnel/network.go5
-rw-r--r--test/apiv2/rest_api/test_rest_v2_0_0.py2
-rw-r--r--test/e2e/network_test.go50
18 files changed, 447 insertions, 22 deletions
diff --git a/cmd/podman/networks/prune.go b/cmd/podman/networks/prune.go
new file mode 100644
index 000000000..d6c7d3a7f
--- /dev/null
+++ b/cmd/podman/networks/prune.go
@@ -0,0 +1,82 @@
+package network
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containers/podman/v2/cmd/podman/common"
+ "github.com/containers/podman/v2/cmd/podman/registry"
+ "github.com/containers/podman/v2/cmd/podman/utils"
+ "github.com/containers/podman/v2/cmd/podman/validate"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ networkPruneDescription = `Prune unused networks`
+ networkPruneCommand = &cobra.Command{
+ Use: "prune [options]",
+ Short: "network prune",
+ Long: networkPruneDescription,
+ RunE: networkPrune,
+ Example: `podman network prune`,
+ Args: validate.NoArgs,
+ ValidArgsFunction: common.AutocompleteNetworks,
+ }
+)
+
+var (
+ networkPruneOptions entities.NetworkPruneOptions
+ force bool
+)
+
+func networkPruneFlags(flags *pflag.FlagSet) {
+ //TODO: Not implemented but for future reference
+ //flags.StringSliceVar(&networkPruneOptions.Filters,"filters", []string{}, "provide filter values (e.g. 'until=<timestamp>')")
+ flags.BoolVarP(&force, "force", "f", false, "do not prompt for confirmation")
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: networkPruneCommand,
+ Parent: networkCmd,
+ })
+ flags := networkPruneCommand.Flags()
+ networkPruneFlags(flags)
+}
+
+func networkPrune(cmd *cobra.Command, _ []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ if !force {
+ reader := bufio.NewReader(os.Stdin)
+ fmt.Println("WARNING! This will remove all networks not used by at least one container.")
+ fmt.Print("Are you sure you want to continue? [y/N] ")
+ answer, err := reader.ReadString('\n')
+ if err != nil {
+ return err
+ }
+ if strings.ToLower(answer)[0] != 'y' {
+ return nil
+ }
+ }
+ responses, err := registry.ContainerEngine().NetworkPrune(registry.Context(), networkPruneOptions)
+ if err != nil {
+ setExitCode(err)
+ return err
+ }
+ for _, r := range responses {
+ if r.Error == nil {
+ fmt.Println(r.Name)
+ } else {
+ setExitCode(r.Error)
+ errs = append(errs, r.Error)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/docs/source/markdown/podman-network-prune.1.md b/docs/source/markdown/podman-network-prune.1.md
new file mode 100644
index 000000000..af0a7295d
--- /dev/null
+++ b/docs/source/markdown/podman-network-prune.1.md
@@ -0,0 +1,31 @@
+% podman-network-prune(1)
+
+## NAME
+podman\-network\-prune - Remove all unused networks
+
+## SYNOPSIS
+**podman network prune** [*options*]
+
+## DESCRIPTION
+Remove all unused networks. An unused network is defined by a network which
+has no containers connected or configured to connect to it. It will not remove
+the so-called default network which goes by the name of *podman*.
+
+## OPTIONS
+#### **--force**, **-f**
+
+Do not prompt for confirmation
+
+## EXAMPLE
+Prune networks
+
+```
+podman network prune
+```
+
+
+## SEE ALSO
+podman(1), podman-network(1), podman-network-remove(1)
+
+## HISTORY
+February 2021, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-network.1.md b/docs/source/markdown/podman-network.1.md
index 3ad37b8bf..885c957b6 100644
--- a/docs/source/markdown/podman-network.1.md
+++ b/docs/source/markdown/podman-network.1.md
@@ -19,6 +19,7 @@ The network command manages CNI networks for Podman.
| exists | [podman-network-exists(1)](podman-network-exists.1.md) | Check if the given network exists |
| inspect | [podman-network-inspect(1)](podman-network-inspect.1.md) | Displays the raw CNI network configuration for one or more networks |
| ls | [podman-network-ls(1)](podman-network-ls.1.md) | Display a summary of CNI networks |
+| prune | [podman-network-prune(1)](podman-network-prune.1.md) | Remove all unused networks |
| reload | [podman-network-reload(1)](podman-network-reload.1.md) | Reload network configuration for containers |
| rm | [podman-network-rm(1)](podman-network-rm.1.md) | Remove one or more CNI networks |
diff --git a/docs/source/network.rst b/docs/source/network.rst
index b5829876e..eb0c2c7f9 100644
--- a/docs/source/network.rst
+++ b/docs/source/network.rst
@@ -13,6 +13,8 @@ Network
:doc:`ls <markdown/podman-network-ls.1>` network list
+:doc:`prune <markdown/podman-network-prune.1>` network prune
+
:doc:`reload <markdown/podman-network-reload.1>` network reload
:doc:`rm <markdown/podman-network-rm.1>` network rm
diff --git a/libpod/network/network.go b/libpod/network/network.go
index 0ff14c1f7..cdaef6c13 100644
--- a/libpod/network/network.go
+++ b/libpod/network/network.go
@@ -11,6 +11,7 @@ import (
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v2/libpod/define"
+ "github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/util"
"github.com/pkg/errors"
@@ -174,14 +175,9 @@ func ValidateUserNetworkIsAvailable(config *config.Config, userNet *net.IPNet) e
return nil
}
-// RemoveNetwork removes a given network by name. If the network has container associated with it, that
-// must be handled outside the context of this.
-func RemoveNetwork(config *config.Config, name string) error {
- l, err := acquireCNILock(config)
- if err != nil {
- return err
- }
- defer l.releaseCNILock()
+// removeNetwork is removes a cni network without a lock and should only be called
+// when a lock was otherwise acquired.
+func removeNetwork(config *config.Config, name string) error {
cniPath, err := GetCNIConfigPathByNameOrID(config, name)
if err != nil {
return err
@@ -213,6 +209,17 @@ func RemoveNetwork(config *config.Config, name string) error {
return nil
}
+// RemoveNetwork removes a given network by name. If the network has container associated with it, that
+// must be handled outside the context of this.
+func RemoveNetwork(config *config.Config, name string) error {
+ l, err := acquireCNILock(config)
+ if err != nil {
+ return err
+ }
+ defer l.releaseCNILock()
+ return removeNetwork(config, name)
+}
+
// InspectNetwork reads a CNI config and returns its configuration
func InspectNetwork(config *config.Config, name string) (map[string]interface{}, error) {
b, err := ReadRawCNIConfByName(config, name)
@@ -243,3 +250,30 @@ func GetNetworkID(name string) string {
hash := sha256.Sum256([]byte(name))
return hex.EncodeToString(hash[:])
}
+
+// PruneNetworks removes networks that are not being used and that is not the default
+// network. To keep proper fencing for imports, you must provide the used networks
+// to this function as a map. the key is meaningful in the map, the book is a no-op
+func PruneNetworks(rtc *config.Config, usedNetworks map[string]bool) ([]*entities.NetworkPruneReport, error) {
+ var reports []*entities.NetworkPruneReport
+ lock, err := acquireCNILock(rtc)
+ if err != nil {
+ return nil, err
+ }
+ defer lock.releaseCNILock()
+ nets, err := GetNetworkNamesFromFileSystem(rtc)
+ if err != nil {
+ return nil, err
+ }
+ for _, n := range nets {
+ _, found := usedNetworks[n]
+ // Remove is not default network and not found in the used list
+ if n != rtc.Network.DefaultNetwork && !found {
+ reports = append(reports, &entities.NetworkPruneReport{
+ Name: n,
+ Error: removeNetwork(rtc, n),
+ })
+ }
+ }
+ return reports, nil
+}
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go
index f0b922885..f7a70816f 100644
--- a/pkg/api/handlers/compat/networks.go
+++ b/pkg/api/handlers/compat/networks.go
@@ -388,3 +388,25 @@ func Disconnect(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, "OK")
}
+
+// Prune removes unused networks
+func Prune(w http.ResponseWriter, r *http.Request) {
+ // TODO Filters are not implemented
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ 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)
+ return
+ }
+ var prunedNetworks []string //nolint
+ for _, pr := range pruneReports {
+ if pr.Error != nil {
+ logrus.Error(pr.Error)
+ continue
+ }
+ prunedNetworks = append(prunedNetworks, pr.Name)
+ }
+ utils.WriteResponse(w, http.StatusOK, prunedNetworks)
+}
diff --git a/pkg/api/handlers/compat/swagger.go b/pkg/api/handlers/compat/swagger.go
index 0a514822b..1d1f1ecf2 100644
--- a/pkg/api/handlers/compat/swagger.go
+++ b/pkg/api/handlers/compat/swagger.go
@@ -77,3 +77,10 @@ type swagCompatNetworkDisconnectRequest struct {
// in:body
Body struct{ types.NetworkDisconnect }
}
+
+// Network prune
+// swagger:response NetworkPruneResponse
+type swagCompatNetworkPruneResponse struct {
+ // in:body
+ Body []string
+}
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index d3bf06988..998f89d96 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -175,3 +175,17 @@ func ExistsNetwork(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
+
+// Prune removes unused networks
+func Prune(w http.ResponseWriter, r *http.Request) {
+ // TODO Filters are not implemented
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ 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)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, pruneReports)
+}
diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go
index 3d9e7fb89..d3345d8da 100644
--- a/pkg/api/server/register_networks.go
+++ b/pkg/api/server/register_networks.go
@@ -9,19 +9,6 @@ import (
)
func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
- // swagger:operation POST /networks/prune compat compatPruneNetwork
- // ---
- // tags:
- // - networks (compat)
- // Summary: Delete unused networks
- // description: Not supported
- // produces:
- // - application/json
- // responses:
- // 404:
- // $ref: "#/responses/NoSuchNetwork"
- r.HandleFunc(VersionedPath("/networks/prune"), compat.UnsupportedHandler).Methods(http.MethodPost)
- r.HandleFunc("/networks/prune", compat.UnsupportedHandler).Methods(http.MethodPost)
// swagger:operation DELETE /networks/{name} compat compatRemoveNetwork
// ---
// tags:
@@ -172,6 +159,35 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/networks/{name}/disconnect"), s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
r.HandleFunc("/networks/{name}/disconnect", s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
+ // swagger:operation POST /networks/prune compat compatPruneNetwork
+ // ---
+ // tags:
+ // - networks (compat)
+ // summary: Delete unused networks
+ // description: Remove CNI networks that do not have containers
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: query
+ // 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.
+ // - label (label=<key>, label=<key>=<value>, label!=<key>, or label!=<key>=<value>) Prune networks with (or without, in case label!=... is used) the specified labels.
+ // responses:
+ // 200:
+ // description: OK
+ // schema:
+ // type: array
+ // items:
+ // type: string
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/networks/prune"), s.APIHandler(compat.Prune)).Methods(http.MethodPost)
+ r.HandleFunc("/networks/prune", s.APIHandler(compat.Prune)).Methods(http.MethodPost)
// swagger:operation DELETE /libpod/networks/{name} libpod libpodRemoveNetwork
// ---
@@ -353,5 +369,29 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/networks/{name}/disconnect"), s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
+ // swagger:operation POST /libpod/networks/prune libpod libpodPruneNetwork
+ // ---
+ // tags:
+ // - networks
+ // summary: Delete unused networks
+ // description: Remove CNI networks that do not have containers
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: query
+ // 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.
+ // - label (label=<key>, label=<key>=<value>, label!=<key>, or label!=<key>=<value>) Prune networks with (or without, in case label!=... is used) the specified labels.
+ // responses:
+ // 200:
+ // $ref: "#/responses/NetworkPruneResponse"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/networks/prune"), s.APIHandler(libpod.Prune)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go
index 8debeee84..428e60cf2 100644
--- a/pkg/bindings/network/network.go
+++ b/pkg/bindings/network/network.go
@@ -180,3 +180,21 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool,
}
return response.IsSuccess(), nil
}
+
+// Prune removes unused CNI networks
+func Prune(ctx context.Context, options *PruneOptions) ([]*entities.NetworkPruneReport, error) {
+ // TODO Filters is not implemented
+ var (
+ prunedNetworks []*entities.NetworkPruneReport
+ )
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ response, err := conn.DoRequest(nil, http.MethodPost, "/networks/prune", nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ return prunedNetworks, response.Process(&prunedNetworks)
+}
diff --git a/pkg/bindings/network/types.go b/pkg/bindings/network/types.go
index 91cbcf044..47dce67c7 100644
--- a/pkg/bindings/network/types.go
+++ b/pkg/bindings/network/types.go
@@ -74,3 +74,9 @@ type ConnectOptions struct {
// if a network exists
type ExistsOptions struct {
}
+
+//go:generate go run ../generator/generator.go PruneOptions
+// PruneOptions are optional options for removing unused
+// CNI networks
+type PruneOptions struct {
+}
diff --git a/pkg/bindings/network/types_prune_options.go b/pkg/bindings/network/types_prune_options.go
new file mode 100644
index 000000000..c56dcd0d3
--- /dev/null
+++ b/pkg/bindings/network/types_prune_options.go
@@ -0,0 +1,75 @@
+package network
+
+import (
+ "net/url"
+ "reflect"
+ "strings"
+
+ "github.com/containers/podman/v2/pkg/bindings/util"
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+)
+
+/*
+This file is generated automatically by go generate. Do not edit.
+*/
+
+// Changed
+func (o *PruneOptions) Changed(fieldName string) bool {
+ r := reflect.ValueOf(o)
+ value := reflect.Indirect(r).FieldByName(fieldName)
+ return !value.IsNil()
+}
+
+// ToParams
+func (o *PruneOptions) ToParams() (url.Values, error) {
+ params := url.Values{}
+ if o == nil {
+ return params, nil
+ }
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ s := reflect.ValueOf(o)
+ if reflect.Ptr == s.Kind() {
+ s = s.Elem()
+ }
+ sType := s.Type()
+ for i := 0; i < s.NumField(); i++ {
+ fieldName := sType.Field(i).Name
+ if !o.Changed(fieldName) {
+ continue
+ }
+ fieldName = strings.ToLower(fieldName)
+ f := s.Field(i)
+ if reflect.Ptr == f.Kind() {
+ f = f.Elem()
+ }
+ switch {
+ case util.IsSimpleType(f):
+ params.Set(fieldName, util.SimpleTypeToParam(f))
+ case f.Kind() == reflect.Slice:
+ for i := 0; i < f.Len(); i++ {
+ elem := f.Index(i)
+ if util.IsSimpleType(elem) {
+ params.Add(fieldName, util.SimpleTypeToParam(elem))
+ } else {
+ return nil, errors.New("slices must contain only simple types")
+ }
+ }
+ case f.Kind() == reflect.Map:
+ lowerCaseKeys := make(map[string][]string)
+ iter := f.MapRange()
+ for iter.Next() {
+ lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string)
+
+ }
+ s, err := json.MarshalToString(lowerCaseKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ params.Set(fieldName, s)
+ }
+
+ }
+ return params, nil
+}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 39bda1d72..2c97d7baf 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -63,6 +63,7 @@ type ContainerEngine interface {
NetworkExists(ctx context.Context, networkname string) (*BoolReport, error)
NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error)
NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error)
+ NetworkPrune(ctx context.Context, options NetworkPruneOptions) ([]*NetworkPruneReport, error)
NetworkReload(ctx context.Context, names []string, options NetworkReloadOptions) ([]*NetworkReloadReport, error)
NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error)
diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go
index b76bfcac7..1859f920e 100644
--- a/pkg/domain/entities/network.go
+++ b/pkg/domain/entities/network.go
@@ -80,3 +80,15 @@ type NetworkConnectOptions struct {
Aliases []string
Container string
}
+
+// NetworkPruneReport containers the name of network and an error
+// associated in its pruning (removal)
+// swagger:model NetworkPruneReport
+type NetworkPruneReport struct {
+ Name string
+ Error error
+}
+
+// NetworkPruneOptions describes options for pruning
+// unused cni networks
+type NetworkPruneOptions struct{}
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
index bc4328fcd..13fabe89d 100644
--- a/pkg/domain/infra/abi/network.go
+++ b/pkg/domain/infra/abi/network.go
@@ -155,3 +155,28 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string
Value: exists,
}, nil
}
+
+// Network prune removes unused cni networks
+func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) {
+ runtimeConfig, err := ic.Libpod.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ cons, err := ic.Libpod.GetAllContainers()
+ if err != nil {
+ return nil, err
+ }
+ // Gather up all the non-default networks that the
+ // containers want
+ usedNetworks := 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
+ }
+ }
+ return network.PruneNetworks(runtimeConfig, usedNetworks)
+}
diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go
index bdb1beb03..990bfa880 100644
--- a/pkg/domain/infra/tunnel/network.go
+++ b/pkg/domain/infra/tunnel/network.go
@@ -89,3 +89,8 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string
Value: exists,
}, nil
}
+
+// Network prune removes unused cni networks
+func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) {
+ return network.Prune(ic.ClientCtx, nil)
+}
diff --git a/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py
index 9ce0803fb..73db35cc1 100644
--- a/test/apiv2/rest_api/test_rest_v2_0_0.py
+++ b/test/apiv2/rest_api/test_rest_v2_0_0.py
@@ -484,7 +484,7 @@ class TestApi(unittest.TestCase):
self.assertEqual(inspect.status_code, 404, inspect.content)
prune = requests.post(PODMAN_URL + "/v1.40/networks/prune")
- self.assertEqual(prune.status_code, 404, prune.content)
+ self.assertEqual(prune.status_code, 200, prune.content)
def test_volumes_compat(self):
name = "Volume_" + "".join(random.choice(string.ascii_letters) for i in range(10))
diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go
index c6010ba43..d4e1a3698 100644
--- a/test/e2e/network_test.go
+++ b/test/e2e/network_test.go
@@ -540,4 +540,54 @@ var _ = Describe("Podman network", func() {
nc.WaitWithDefaultTimeout()
Expect(nc.ExitCode()).To(Equal(0))
})
+
+ It("podman network prune", func() {
+ // Create two networks
+ // Check they are there
+ // Run a container on one of them
+ // Network Prune
+ // Check that one has been pruned, other remains
+ net := "macvlan" + stringid.GenerateNonCryptoID()
+ net1 := net + "1"
+ net2 := net + "2"
+ nc := podmanTest.Podman([]string{"network", "create", net1})
+ nc.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(net1)
+ Expect(nc.ExitCode()).To(Equal(0))
+
+ nc2 := podmanTest.Podman([]string{"network", "create", net2})
+ nc2.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(net2)
+ Expect(nc2.ExitCode()).To(Equal(0))
+
+ list := podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}}"})
+ list.WaitWithDefaultTimeout()
+ Expect(list.ExitCode()).To(BeZero())
+
+ Expect(StringInSlice(net1, list.OutputToStringArray())).To(BeTrue())
+ Expect(StringInSlice(net2, list.OutputToStringArray())).To(BeTrue())
+ if !isRootless() {
+ Expect(StringInSlice("podman", list.OutputToStringArray())).To(BeTrue())
+ }
+
+ session := podmanTest.Podman([]string{"run", "-dt", "--net", net2, ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ prune := podmanTest.Podman([]string{"network", "prune", "-f"})
+ prune.WaitWithDefaultTimeout()
+ Expect(prune.ExitCode()).To(BeZero())
+
+ listAgain := podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}}"})
+ listAgain.WaitWithDefaultTimeout()
+ Expect(listAgain.ExitCode()).To(BeZero())
+
+ Expect(StringInSlice(net1, listAgain.OutputToStringArray())).To(BeFalse())
+ Expect(StringInSlice(net2, listAgain.OutputToStringArray())).To(BeTrue())
+ // Make sure default network 'podman' still exists
+ if !isRootless() {
+ Expect(StringInSlice("podman", list.OutputToStringArray())).To(BeTrue())
+ }
+
+ })
})