mirror of
https://github.com/containers/podman.git
synced 2025-06-25 12:20:42 +08:00
82
cmd/podman/networks/prune.go
Normal file
82
cmd/podman/networks/prune.go
Normal file
@ -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
docs/source/markdown/podman-network-prune.1.md
Normal file
31
docs/source/markdown/podman-network-prune.1.md
Normal file
@ -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>
|
@ -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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| rm | [podman-network-rm(1)](podman-network-rm.1.md) | Remove one or more CNI networks |
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ Network
|
|||||||
|
|
||||||
:doc:`ls <markdown/podman-network-ls.1>` network list
|
: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:`reload <markdown/podman-network-reload.1>` network reload
|
||||||
|
|
||||||
:doc:`rm <markdown/podman-network-rm.1>` network rm
|
:doc:`rm <markdown/podman-network-rm.1>` network rm
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
|
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
|
||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/podman/v2/libpod/define"
|
"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/rootless"
|
||||||
"github.com/containers/podman/v2/pkg/util"
|
"github.com/containers/podman/v2/pkg/util"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -174,14 +175,9 @@ func ValidateUserNetworkIsAvailable(config *config.Config, userNet *net.IPNet) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveNetwork removes a given network by name. If the network has container associated with it, that
|
// removeNetwork is removes a cni network without a lock and should only be called
|
||||||
// must be handled outside the context of this.
|
// when a lock was otherwise acquired.
|
||||||
func RemoveNetwork(config *config.Config, name string) error {
|
func removeNetwork(config *config.Config, name string) error {
|
||||||
l, err := acquireCNILock(config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer l.releaseCNILock()
|
|
||||||
cniPath, err := GetCNIConfigPathByNameOrID(config, name)
|
cniPath, err := GetCNIConfigPathByNameOrID(config, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -213,6 +209,17 @@ func RemoveNetwork(config *config.Config, name string) error {
|
|||||||
return nil
|
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
|
// InspectNetwork reads a CNI config and returns its configuration
|
||||||
func InspectNetwork(config *config.Config, name string) (map[string]interface{}, error) {
|
func InspectNetwork(config *config.Config, name string) (map[string]interface{}, error) {
|
||||||
b, err := ReadRawCNIConfByName(config, name)
|
b, err := ReadRawCNIConfByName(config, name)
|
||||||
@ -243,3 +250,30 @@ func GetNetworkID(name string) string {
|
|||||||
hash := sha256.Sum256([]byte(name))
|
hash := sha256.Sum256([]byte(name))
|
||||||
return hex.EncodeToString(hash[:])
|
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
|
||||||
|
}
|
||||||
|
@ -388,3 +388,25 @@ func Disconnect(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
utils.WriteResponse(w, http.StatusOK, "OK")
|
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)
|
||||||
|
}
|
||||||
|
@ -77,3 +77,10 @@ type swagCompatNetworkDisconnectRequest struct {
|
|||||||
// in:body
|
// in:body
|
||||||
Body struct{ types.NetworkDisconnect }
|
Body struct{ types.NetworkDisconnect }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Network prune
|
||||||
|
// swagger:response NetworkPruneResponse
|
||||||
|
type swagCompatNetworkPruneResponse struct {
|
||||||
|
// in:body
|
||||||
|
Body []string
|
||||||
|
}
|
||||||
|
@ -175,3 +175,17 @@ func ExistsNetwork(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
utils.WriteResponse(w, http.StatusNoContent, "")
|
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)
|
||||||
|
}
|
||||||
|
@ -9,19 +9,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
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
|
// swagger:operation DELETE /networks/{name} compat compatRemoveNetwork
|
||||||
// ---
|
// ---
|
||||||
// tags:
|
// tags:
|
||||||
@ -172,6 +159,35 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
|||||||
// $ref: "#/responses/InternalError"
|
// $ref: "#/responses/InternalError"
|
||||||
r.HandleFunc(VersionedPath("/networks/{name}/disconnect"), s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
|
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)
|
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
|
// swagger:operation DELETE /libpod/networks/{name} libpod libpodRemoveNetwork
|
||||||
// ---
|
// ---
|
||||||
@ -353,5 +369,29 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
|||||||
// 500:
|
// 500:
|
||||||
// $ref: "#/responses/InternalError"
|
// $ref: "#/responses/InternalError"
|
||||||
r.HandleFunc(VersionedPath("/libpod/networks/{name}/disconnect"), s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -180,3 +180,21 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool,
|
|||||||
}
|
}
|
||||||
return response.IsSuccess(), nil
|
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)
|
||||||
|
}
|
||||||
|
@ -74,3 +74,9 @@ type ConnectOptions struct {
|
|||||||
// if a network exists
|
// if a network exists
|
||||||
type ExistsOptions struct {
|
type ExistsOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:generate go run ../generator/generator.go PruneOptions
|
||||||
|
// PruneOptions are optional options for removing unused
|
||||||
|
// CNI networks
|
||||||
|
type PruneOptions struct {
|
||||||
|
}
|
||||||
|
75
pkg/bindings/network/types_prune_options.go
Normal file
75
pkg/bindings/network/types_prune_options.go
Normal file
@ -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
|
||||||
|
}
|
@ -63,6 +63,7 @@ type ContainerEngine interface {
|
|||||||
NetworkExists(ctx context.Context, networkname string) (*BoolReport, error)
|
NetworkExists(ctx context.Context, networkname string) (*BoolReport, error)
|
||||||
NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error)
|
NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error)
|
||||||
NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, 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)
|
NetworkReload(ctx context.Context, names []string, options NetworkReloadOptions) ([]*NetworkReloadReport, error)
|
||||||
NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
|
NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
|
||||||
PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error)
|
PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error)
|
||||||
|
@ -80,3 +80,15 @@ type NetworkConnectOptions struct {
|
|||||||
Aliases []string
|
Aliases []string
|
||||||
Container 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{}
|
||||||
|
@ -155,3 +155,28 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string
|
|||||||
Value: exists,
|
Value: exists,
|
||||||
}, nil
|
}, 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)
|
||||||
|
}
|
||||||
|
@ -89,3 +89,8 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string
|
|||||||
Value: exists,
|
Value: exists,
|
||||||
}, nil
|
}, 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)
|
||||||
|
}
|
||||||
|
@ -484,7 +484,7 @@ class TestApi(unittest.TestCase):
|
|||||||
self.assertEqual(inspect.status_code, 404, inspect.content)
|
self.assertEqual(inspect.status_code, 404, inspect.content)
|
||||||
|
|
||||||
prune = requests.post(PODMAN_URL + "/v1.40/networks/prune")
|
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):
|
def test_volumes_compat(self):
|
||||||
name = "Volume_" + "".join(random.choice(string.ascii_letters) for i in range(10))
|
name = "Volume_" + "".join(random.choice(string.ascii_letters) for i in range(10))
|
||||||
|
@ -540,4 +540,54 @@ var _ = Describe("Podman network", func() {
|
|||||||
nc.WaitWithDefaultTimeout()
|
nc.WaitWithDefaultTimeout()
|
||||||
Expect(nc.ExitCode()).To(Equal(0))
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user