Skip to content

Commit

Permalink
add network prune
Browse files Browse the repository at this point in the history
add the ability to prune unused cni networks.  filters are not implemented
but included both compat and podman api endpoints.

Fixes :containers#8673

Signed-off-by: baude <[email protected]>
  • Loading branch information
baude committed Feb 6, 2021
1 parent c421127 commit 91ea3fa
Show file tree
Hide file tree
Showing 18 changed files with 447 additions and 22 deletions.
82 changes: 82 additions & 0 deletions cmd/podman/networks/prune.go
Original file line number Diff line number Diff line change
@@ -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()
}
31 changes: 31 additions & 0 deletions docs/source/markdown/podman-network-prune.1.md
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
1 change: 1 addition & 0 deletions docs/source/markdown/podman-network.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down
2 changes: 2 additions & 0 deletions docs/source/network.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
50 changes: 42 additions & 8 deletions libpod/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
22 changes: 22 additions & 0 deletions pkg/api/handlers/compat/networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
7 changes: 7 additions & 0 deletions pkg/api/handlers/compat/swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
14 changes: 14 additions & 0 deletions pkg/api/handlers/libpod/networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
66 changes: 53 additions & 13 deletions pkg/api/server/register_networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
// ---
Expand Down Expand Up @@ -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
}
18 changes: 18 additions & 0 deletions pkg/bindings/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
6 changes: 6 additions & 0 deletions pkg/bindings/network/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
}
Loading

0 comments on commit 91ea3fa

Please sign in to comment.