Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add network prune #9236

Merged
merged 1 commit into from
Feb 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have a --unused option for podman network list or an option here (--preview?) to show the ones that would be pruned if the command was called.

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
baude marked this conversation as resolved.
Show resolved Hide resolved
// 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
baude marked this conversation as resolved.
Show resolved Hide resolved
// 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