From 7e24c16086a9a4f38e241e51837f2be4877c04a6 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Tue, 18 Oct 2016 04:36:52 +0000 Subject: [PATCH] add `docker network prune` `docker network prune` prunes unused networks, including overlay ones. `docker system prune` also prunes unused networks. Signed-off-by: Akihiro Suda --- api/server/router/network/backend.go | 1 + api/server/router/network/network.go | 1 + api/server/router/network/network_routes.go | 21 ++++++ api/types/types.go | 11 +++ cli/command/network/cmd.go | 1 + cli/command/network/prune.go | 72 ++++++++++++++++++ cli/command/prune/prune.go | 11 +++ cli/command/system/prune.go | 6 +- client/interface.go | 1 + client/network_prune.go | 26 +++++++ cmd/dockerd/daemon.go | 1 + daemon/cluster.go | 12 +++ daemon/daemon.go | 11 +++ daemon/prune.go | 73 +++++++++++++++++++ docs/reference/api/docker_remote_api.md | 2 + docs/reference/api/docker_remote_api_v1.25.md | 30 ++++++++ docs/reference/commandline/container_prune.md | 1 + docs/reference/commandline/image_prune.md | 1 + docs/reference/commandline/network_connect.md | 1 + docs/reference/commandline/network_create.md | 1 + .../commandline/network_disconnect.md | 1 + docs/reference/commandline/network_inspect.md | 1 + docs/reference/commandline/network_ls.md | 1 + docs/reference/commandline/network_prune.md | 45 ++++++++++++ docs/reference/commandline/network_rm.md | 1 + docs/reference/commandline/system_df.md | 3 + docs/reference/commandline/system_prune.md | 4 +- docs/reference/commandline/volume_prune.md | 1 + integration-cli/docker_cli_prune_unix_test.go | 61 ++++++++++++++++ 29 files changed, 399 insertions(+), 3 deletions(-) create mode 100644 cli/command/network/prune.go create mode 100644 client/network_prune.go create mode 100644 daemon/cluster.go create mode 100644 docs/reference/commandline/network_prune.md create mode 100644 integration-cli/docker_cli_prune_unix_test.go diff --git a/api/server/router/network/backend.go b/api/server/router/network/backend.go index 280ca07efd..cf82398c93 100644 --- a/api/server/router/network/backend.go +++ b/api/server/router/network/backend.go @@ -17,4 +17,5 @@ type Backend interface { ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error DeleteNetwork(name string) error + NetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) } diff --git a/api/server/router/network/network.go b/api/server/router/network/network.go index f940ae0a26..08a5c8c6a6 100644 --- a/api/server/router/network/network.go +++ b/api/server/router/network/network.go @@ -37,6 +37,7 @@ func (r *networkRouter) initRoutes() { router.NewPostRoute("/networks/create", r.postNetworkCreate), router.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect), router.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect), + router.NewPostRoute("/networks/prune", r.postNetworksPrune), // DELETE router.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork), } diff --git a/api/server/router/network/network_routes.go b/api/server/router/network/network_routes.go index 4a7afb86ef..2d9c786689 100644 --- a/api/server/router/network/network_routes.go +++ b/api/server/router/network/network_routes.go @@ -274,3 +274,24 @@ func buildEndpointResource(id string, name string, info libnetwork.EndpointInfo) } return er } + +func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if err := httputils.ParseForm(r); err != nil { + return err + } + + if err := httputils.CheckForJSON(r); err != nil { + return err + } + + var cfg types.NetworksPruneConfig + if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { + return err + } + + pruneReport, err := n.backend.NetworksPrune(&cfg) + if err != nil { + return err + } + return httputils.WriteJSON(w, http.StatusOK, pruneReport) +} diff --git a/api/types/types.go b/api/types/types.go index afb3e8bc91..fee9e3cb65 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -522,6 +522,11 @@ type ContainersPruneConfig struct { type VolumesPruneConfig struct { } +// NetworksPruneConfig contains the configuration for Remote API: +// POST "/networks/prune" +type NetworksPruneConfig struct { +} + // ContainersPruneReport contains the response for Remote API: // POST "/containers/prune" type ContainersPruneReport struct { @@ -542,3 +547,9 @@ type ImagesPruneReport struct { ImagesDeleted []ImageDelete SpaceReclaimed uint64 } + +// NetworksPruneReport contains the response for Remote API: +// POST "/networks/prune" +type NetworksPruneReport struct { + NetworksDeleted []string +} diff --git a/cli/command/network/cmd.go b/cli/command/network/cmd.go index b33f98cd30..77c8e4908e 100644 --- a/cli/command/network/cmd.go +++ b/cli/command/network/cmd.go @@ -26,6 +26,7 @@ func NewNetworkCommand(dockerCli *command.DockerCli) *cobra.Command { newInspectCommand(dockerCli), newListCommand(dockerCli), newRemoveCommand(dockerCli), + NewPruneCommand(dockerCli), ) return cmd } diff --git a/cli/command/network/prune.go b/cli/command/network/prune.go new file mode 100644 index 0000000000..00e05d3bdf --- /dev/null +++ b/cli/command/network/prune.go @@ -0,0 +1,72 @@ +package network + +import ( + "fmt" + + "golang.org/x/net/context" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/cli" + "github.com/docker/docker/cli/command" + "github.com/spf13/cobra" +) + +type pruneOptions struct { + force bool +} + +// NewPruneCommand returns a new cobra prune command for networks +func NewPruneCommand(dockerCli *command.DockerCli) *cobra.Command { + var opts pruneOptions + + cmd := &cobra.Command{ + Use: "prune [OPTIONS]", + Short: "Remove all unused networks", + Args: cli.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + output, err := runPrune(dockerCli, opts) + if err != nil { + return err + } + if output != "" { + fmt.Fprintln(dockerCli.Out(), output) + } + return nil + }, + } + + flags := cmd.Flags() + flags.BoolVarP(&opts.force, "force", "f", false, "Do not prompt for confirmation") + + return cmd +} + +const warning = `WARNING! This will remove all networks not used by at least one container. +Are you sure you want to continue?` + +func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (output string, err error) { + if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) { + return + } + + report, err := dockerCli.Client().NetworksPrune(context.Background(), types.NetworksPruneConfig{}) + if err != nil { + return + } + + if len(report.NetworksDeleted) > 0 { + output = "Deleted Networks:\n" + for _, id := range report.NetworksDeleted { + output += id + "\n" + } + } + + return +} + +// RunPrune calls the Network Prune API +// This returns the amount of space reclaimed and a detailed output string +func RunPrune(dockerCli *command.DockerCli) (uint64, string, error) { + output, err := runPrune(dockerCli, pruneOptions{force: true}) + return 0, output, err +} diff --git a/cli/command/prune/prune.go b/cli/command/prune/prune.go index fd04c590b6..a022487fd6 100644 --- a/cli/command/prune/prune.go +++ b/cli/command/prune/prune.go @@ -4,6 +4,7 @@ import ( "github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command/container" "github.com/docker/docker/cli/command/image" + "github.com/docker/docker/cli/command/network" "github.com/docker/docker/cli/command/volume" "github.com/spf13/cobra" ) @@ -23,6 +24,11 @@ func NewImagePruneCommand(dockerCli *command.DockerCli) *cobra.Command { return image.NewPruneCommand(dockerCli) } +// NewNetworkPruneCommand returns a cobra prune command for Networks +func NewNetworkPruneCommand(dockerCli *command.DockerCli) *cobra.Command { + return network.NewPruneCommand(dockerCli) +} + // RunContainerPrune executes a prune command for containers func RunContainerPrune(dockerCli *command.DockerCli) (uint64, string, error) { return container.RunPrune(dockerCli) @@ -37,3 +43,8 @@ func RunVolumePrune(dockerCli *command.DockerCli) (uint64, string, error) { func RunImagePrune(dockerCli *command.DockerCli, all bool) (uint64, string, error) { return image.RunPrune(dockerCli, all) } + +// RunNetworkPrune executes a prune command for networks +func RunNetworkPrune(dockerCli *command.DockerCli) (uint64, string, error) { + return network.RunPrune(dockerCli) +} diff --git a/cli/command/system/prune.go b/cli/command/system/prune.go index ea8a41380f..c79bc6910e 100644 --- a/cli/command/system/prune.go +++ b/cli/command/system/prune.go @@ -39,6 +39,7 @@ const ( warning = `WARNING! This will remove: - all stopped containers - all volumes not used by at least one container + - all networks not used by at least one container %s Are you sure you want to continue?` @@ -64,13 +65,14 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) error { for _, pruneFn := range []func(dockerCli *command.DockerCli) (uint64, string, error){ prune.RunContainerPrune, prune.RunVolumePrune, + prune.RunNetworkPrune, } { spc, output, err := pruneFn(dockerCli) if err != nil { return err } - if spc > 0 { - spaceReclaimed += spc + spaceReclaimed += spc + if output != "" { fmt.Fprintln(dockerCli.Out(), output) } } diff --git a/client/interface.go b/client/interface.go index f919612163..8abdb0f6fc 100644 --- a/client/interface.go +++ b/client/interface.go @@ -91,6 +91,7 @@ type NetworkAPIClient interface { NetworkInspectWithRaw(ctx context.Context, networkID string) (types.NetworkResource, []byte, error) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) NetworkRemove(ctx context.Context, networkID string) error + NetworksPrune(ctx context.Context, cfg types.NetworksPruneConfig) (types.NetworksPruneReport, error) } // NodeAPIClient defines API client methods for the nodes diff --git a/client/network_prune.go b/client/network_prune.go new file mode 100644 index 0000000000..01185f2e02 --- /dev/null +++ b/client/network_prune.go @@ -0,0 +1,26 @@ +package client + +import ( + "encoding/json" + "fmt" + + "github.com/docker/docker/api/types" + "golang.org/x/net/context" +) + +// NetworksPrune requests the daemon to delete unused networks +func (cli *Client) NetworksPrune(ctx context.Context, cfg types.NetworksPruneConfig) (types.NetworksPruneReport, error) { + var report types.NetworksPruneReport + + serverResp, err := cli.post(ctx, "/networks/prune", nil, cfg, nil) + if err != nil { + return report, err + } + defer ensureReaderClosed(serverResp) + + if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { + return report, fmt.Errorf("Error retrieving network prune report: %v", err) + } + + return report, nil +} diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go index f50f837b28..cfdf3d6970 100644 --- a/cmd/dockerd/daemon.go +++ b/cmd/dockerd/daemon.go @@ -279,6 +279,7 @@ func (cli *DaemonCli) start(opts daemonOptions) (err error) { // initMiddlewares needs cli.d to be populated. Dont change this init order. cli.initMiddlewares(api, serverConfig) + d.SetCluster(c) initRouter(api, d, c) cli.setupConfigReloadTrap() diff --git a/daemon/cluster.go b/daemon/cluster.go new file mode 100644 index 0000000000..98b2aa1e04 --- /dev/null +++ b/daemon/cluster.go @@ -0,0 +1,12 @@ +package daemon + +import ( + apitypes "github.com/docker/docker/api/types" +) + +// Cluster is the interface for github.com/docker/docker/daemon/cluster.(*Cluster). +type Cluster interface { + GetNetwork(input string) (apitypes.NetworkResource, error) + GetNetworks() ([]apitypes.NetworkResource, error) + RemoveNetwork(input string) error +} diff --git a/daemon/daemon.go b/daemon/daemon.go index 8043aea1a5..423cf74827 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -102,6 +102,7 @@ type Daemon struct { containerdRemote libcontainerd.Remote defaultIsolation containertypes.Isolation // Default isolation mode on Windows clusterProvider cluster.Provider + cluster Cluster } // HasExperimental returns whether the experimental features of the daemon are enabled or not @@ -1234,3 +1235,13 @@ func copyBlkioEntry(entries []*containerd.BlkioStatsEntry) []types.BlkioStatEntr } return out } + +// GetCluster returns the cluster +func (daemon *Daemon) GetCluster() Cluster { + return daemon.cluster +} + +// SetCluster sets the cluster +func (daemon *Daemon) SetCluster(cluster Cluster) { + daemon.cluster = cluster +} diff --git a/daemon/prune.go b/daemon/prune.go index cdab7df1bb..953a568d91 100644 --- a/daemon/prune.go +++ b/daemon/prune.go @@ -1,6 +1,8 @@ package daemon import ( + "regexp" + "github.com/Sirupsen/logrus" "github.com/docker/distribution/digest" "github.com/docker/docker/api/types" @@ -8,7 +10,9 @@ import ( "github.com/docker/docker/layer" "github.com/docker/docker/pkg/directory" "github.com/docker/docker/reference" + "github.com/docker/docker/runconfig" "github.com/docker/docker/volume" + "github.com/docker/libnetwork" ) // ContainersPrune removes unused containers @@ -150,3 +154,72 @@ func (daemon *Daemon) ImagesPrune(config *types.ImagesPruneConfig) (*types.Image return rep, nil } + +// localNetworksPrune removes unused local networks +func (daemon *Daemon) localNetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) { + rep := &types.NetworksPruneReport{} + var err error + // When the function returns true, the walk will stop. + l := func(nw libnetwork.Network) bool { + nwName := nw.Name() + predefined := runconfig.IsPreDefinedNetwork(nwName) + if !predefined && len(nw.Endpoints()) == 0 { + if err = daemon.DeleteNetwork(nw.ID()); err != nil { + logrus.Warnf("could not remove network %s: %v", nwName, err) + return false + } + rep.NetworksDeleted = append(rep.NetworksDeleted, nwName) + } + return false + } + daemon.netController.WalkNetworks(l) + return rep, err +} + +// clusterNetworksPrune removes unused cluster networks +func (daemon *Daemon) clusterNetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) { + rep := &types.NetworksPruneReport{} + cluster := daemon.GetCluster() + networks, err := cluster.GetNetworks() + if err != nil { + return rep, err + } + networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`) + for _, nw := range networks { + if nw.Name == "ingress" { + continue + } + // https://github.com/docker/docker/issues/24186 + // `docker network inspect` unfortunately displays ONLY those containers that are local to that node. + // So we try to remove it anyway and check the error + err = cluster.RemoveNetwork(nw.ID) + if err != nil { + // we can safely ignore the "network .. is in use" error + match := networkIsInUse.FindStringSubmatch(err.Error()) + if len(match) != 2 || match[1] != nw.ID { + logrus.Warnf("could not remove network %s: %v", nw.Name, err) + } + continue + } + rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name) + } + return rep, nil +} + +// NetworksPrune removes unused networks +func (daemon *Daemon) NetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) { + rep := &types.NetworksPruneReport{} + clusterRep, err := daemon.clusterNetworksPrune(config) + if err != nil { + logrus.Warnf("could not remove cluster networks: %v", err) + } else { + rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...) + } + localRep, err := daemon.localNetworksPrune(config) + if err != nil { + logrus.Warnf("could not remove local networks: %v", err) + } else { + rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...) + } + return rep, err +} diff --git a/docs/reference/api/docker_remote_api.md b/docs/reference/api/docker_remote_api.md index 740935e248..0d6c4b85d7 100644 --- a/docs/reference/api/docker_remote_api.md +++ b/docs/reference/api/docker_remote_api.md @@ -157,8 +157,10 @@ This section lists each version from latest to oldest. Each listing includes a * `POST /containers/prune` prunes stopped containers. * `POST /images/prune` prunes unused images. * `POST /volumes/prune` prunes unused volumes. +* `POST /networks/prune` prunes unused networks. * Every API response now includes a `Docker-Experimental` header specifying if experimental features are enabled (value can be `true` or `false`). + ### v1.24 API changes [Docker Remote API v1.24](docker_remote_api_v1.24.md) documentation diff --git a/docs/reference/api/docker_remote_api_v1.25.md b/docs/reference/api/docker_remote_api_v1.25.md index 47bc7efe48..e9abd95c10 100644 --- a/docs/reference/api/docker_remote_api_v1.25.md +++ b/docs/reference/api/docker_remote_api_v1.25.md @@ -3881,6 +3881,36 @@ Instruct the driver to remove the network (`id`). - **404** - no such network - **500** - server error +### Prune unused networks + +`POST /networks/prune` + +Delete unused networks + +**Example request**: + + POST /networks/prune HTTP/1.1 + Content-Type: application/json + + { + } + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "NetworksDeleted": [ + "n1" + ], + } + +**Status codes**: + +- **200** – no error +- **500** – server error + ## 3.6 Plugins ### List plugins diff --git a/docs/reference/commandline/container_prune.md b/docs/reference/commandline/container_prune.md index c63324d3ed..3332d7653d 100644 --- a/docs/reference/commandline/container_prune.md +++ b/docs/reference/commandline/container_prune.md @@ -43,4 +43,5 @@ Total reclaimed space: 212 B * [system df](system_df.md) * [volume prune](volume_prune.md) * [image prune](image_prune.md) +* [network prune](network_prune.md) * [system prune](system_prune.md) diff --git a/docs/reference/commandline/image_prune.md b/docs/reference/commandline/image_prune.md index 98c7c4bdf5..5620bd6b5c 100644 --- a/docs/reference/commandline/image_prune.md +++ b/docs/reference/commandline/image_prune.md @@ -67,4 +67,5 @@ Total reclaimed space: 16.43 MB * [system df](system_df.md) * [container prune](container_prune.md) * [volume prune](volume_prune.md) +* [network prune](network_prune.md) * [system prune](system_prune.md) diff --git a/docs/reference/commandline/network_connect.md b/docs/reference/commandline/network_connect.md index 68851f417f..b4a79fc497 100644 --- a/docs/reference/commandline/network_connect.md +++ b/docs/reference/commandline/network_connect.md @@ -98,5 +98,6 @@ You can connect a container to one or more networks. The networks need not be th * [network disconnect](network_disconnect.md) * [network ls](network_ls.md) * [network rm](network_rm.md) +* [network prune](network_prune.md) * [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) * [Work with networks](https://docs.docker.com/engine/userguide/networking/work-with-networks/) diff --git a/docs/reference/commandline/network_create.md b/docs/reference/commandline/network_create.md index 03b891914c..4273207ea8 100644 --- a/docs/reference/commandline/network_create.md +++ b/docs/reference/commandline/network_create.md @@ -197,4 +197,5 @@ to create an externally isolated `overlay` network, you can specify the * [network disconnect](network_disconnect.md) * [network ls](network_ls.md) * [network rm](network_rm.md) +* [network prune](network_prune.md) * [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) diff --git a/docs/reference/commandline/network_disconnect.md b/docs/reference/commandline/network_disconnect.md index f90ea960e6..1f6a00bf45 100644 --- a/docs/reference/commandline/network_disconnect.md +++ b/docs/reference/commandline/network_disconnect.md @@ -39,4 +39,5 @@ Disconnects a container from a network. The container must be running to disconn * [network create](network_create.md) * [network ls](network_ls.md) * [network rm](network_rm.md) +* [network prune](network_prune.md) * [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) diff --git a/docs/reference/commandline/network_inspect.md b/docs/reference/commandline/network_inspect.md index c0ffa2e4a1..b1531b8f65 100644 --- a/docs/reference/commandline/network_inspect.md +++ b/docs/reference/commandline/network_inspect.md @@ -128,4 +128,5 @@ $ docker network inspect simple-network * [network create](network_create.md) * [network ls](network_ls.md) * [network rm](network_rm.md) +* [network prune](network_prune.md) * [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) diff --git a/docs/reference/commandline/network_ls.md b/docs/reference/commandline/network_ls.md index a8fc68ca13..cf3bab6452 100644 --- a/docs/reference/commandline/network_ls.md +++ b/docs/reference/commandline/network_ls.md @@ -214,4 +214,5 @@ d1584f8dc718: host * [network create](network_create.md) * [network inspect](network_inspect.md) * [network rm](network_rm.md) +* [network prune](network_prune.md) * [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) diff --git a/docs/reference/commandline/network_prune.md b/docs/reference/commandline/network_prune.md new file mode 100644 index 0000000000..82da94e31c --- /dev/null +++ b/docs/reference/commandline/network_prune.md @@ -0,0 +1,45 @@ +--- +title: "network prune" +description: "Remove unused networks" +keywords: [network, prune, delete] +--- + +# network prune + +```markdown +Usage: docker network prune [OPTIONS] + +Remove all unused networks + +Options: + -f, --force Do not prompt for confirmation + --help Print usage +``` + +Remove all unused networks. Unused networks are those which are not referenced by any containers. + +Example output: + +```bash +$ docker network prune +WARNING! This will remove all networks not used by at least one container. +Are you sure you want to continue? [y/N] y +Deleted Networks: +n1 +n2 +``` + +## Related information + +* [network disconnect ](network_disconnect.md) +* [network connect](network_connect.md) +* [network create](network_create.md) +* [network ls](network_ls.md) +* [network inspect](network_inspect.md) +* [network rm](network_rm.md) +* [Understand Docker container networks](../../userguide/networking/index.md) +* [system df](system_df.md) +* [container prune](container_prune.md) +* [image prune](image_prune.md) +* [volume prune](volume_prune.md) +* [system prune](system_prune.md) diff --git a/docs/reference/commandline/network_rm.md b/docs/reference/commandline/network_rm.md index a806983610..d2bce4e0f0 100644 --- a/docs/reference/commandline/network_rm.md +++ b/docs/reference/commandline/network_rm.md @@ -55,4 +55,5 @@ deletion. * [network create](network_create.md) * [network ls](network_ls.md) * [network inspect](network_inspect.md) +* [network prune](network_prune.md) * [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) diff --git a/docs/reference/commandline/system_df.md b/docs/reference/commandline/system_df.md index 044f5e4b66..10cd1b40a0 100644 --- a/docs/reference/commandline/system_df.md +++ b/docs/reference/commandline/system_df.md @@ -66,8 +66,11 @@ my-named-vol 0 * `UNIQUE SIZE` is the amount of space that is only used by a given image * `SIZE` is the virtual size of the image, it is the sum of `SHARED SIZE` and `UNIQUE SIZE` +Note that network information is not shown because it doesn't consume the disk space. + ## Related Information * [system prune](system_prune.md) * [container prune](container_prune.md) * [volume prune](volume_prune.md) * [image prune](image_prune.md) +* [network prune](network_prune.md) diff --git a/docs/reference/commandline/system_prune.md b/docs/reference/commandline/system_prune.md index 862b64d1f1..3f0c97478a 100644 --- a/docs/reference/commandline/system_prune.md +++ b/docs/reference/commandline/system_prune.md @@ -26,7 +26,7 @@ Options: --help Print usage ``` -Remove all unused containers, volumes and images (both dangling and unreferenced). +Remove all unused containers, volumes, networks and images (both dangling and unreferenced). Example output: @@ -35,6 +35,7 @@ $ docker system prune -a WARNING! This will remove: - all stopped containers - all volumes not used by at least one container + - all networks not used by at least one container - all images without at least one container associated to them Are you sure you want to continue? [y/N] y Deleted Containers: @@ -74,4 +75,5 @@ Total reclaimed space: 13.5 MB * [system df](system_df.md) * [container prune](container_prune.md) * [image prune](image_prune.md) +* [network prune](network_prune.md) * [system prune](system_prune.md) diff --git a/docs/reference/commandline/volume_prune.md b/docs/reference/commandline/volume_prune.md index 80156296b2..e6866bf244 100644 --- a/docs/reference/commandline/volume_prune.md +++ b/docs/reference/commandline/volume_prune.md @@ -50,4 +50,5 @@ Total reclaimed space: 36 B * [system df](system_df.md) * [container prune](container_prune.md) * [image prune](image_prune.md) +* [network prune](network_prune.md) * [system prune](system_prune.md) diff --git a/integration-cli/docker_cli_prune_unix_test.go b/integration-cli/docker_cli_prune_unix_test.go new file mode 100644 index 0000000000..5585cab302 --- /dev/null +++ b/integration-cli/docker_cli_prune_unix_test.go @@ -0,0 +1,61 @@ +// +build !windows + +package main + +import ( + "strconv" + "strings" + + "github.com/docker/docker/pkg/integration/checker" + "github.com/go-check/check" +) + +func pruneNetworkAndVerify(c *check.C, d *SwarmDaemon, kept, pruned []string) { + _, err := d.Cmd("network", "prune", "--force") + c.Assert(err, checker.IsNil) + out, err := d.Cmd("network", "ls", "--format", "{{.Name}}") + c.Assert(err, checker.IsNil) + for _, s := range kept { + c.Assert(out, checker.Contains, s) + } + for _, s := range pruned { + c.Assert(out, checker.Not(checker.Contains), s) + } +} + +func (s *DockerSwarmSuite) TestPruneNetwork(c *check.C) { + d := s.AddDaemon(c, true, true) + _, err := d.Cmd("network", "create", "n1") // used by container (testprune) + c.Assert(err, checker.IsNil) + _, err = d.Cmd("network", "create", "n2") + c.Assert(err, checker.IsNil) + _, err = d.Cmd("network", "create", "n3", "--driver", "overlay") // used by service (testprunesvc) + c.Assert(err, checker.IsNil) + _, err = d.Cmd("network", "create", "n4", "--driver", "overlay") + c.Assert(err, checker.IsNil) + + cName := "testprune" + _, err = d.Cmd("run", "-d", "--name", cName, "--net", "n1", "busybox", "top") + c.Assert(err, checker.IsNil) + + serviceName := "testprunesvc" + replicas := 1 + out, err := d.Cmd("service", "create", "--name", serviceName, + "--replicas", strconv.Itoa(replicas), + "--network", "n3", + "busybox", "top") + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") + waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, replicas+1) + + // prune and verify + pruneNetworkAndVerify(c, d, []string{"n1", "n3"}, []string{"n2", "n4"}) + + // remove containers, then prune and verify again + _, err = d.Cmd("rm", "-f", cName) + c.Assert(err, checker.IsNil) + _, err = d.Cmd("service", "rm", serviceName) + c.Assert(err, checker.IsNil) + waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 0) + pruneNetworkAndVerify(c, d, []string{}, []string{"n1", "n3"}) +}