diff --git a/api/server/router/container/backend.go b/api/server/router/container/backend.go index 5b4134eba5..0d20188ccf 100644 --- a/api/server/router/container/backend.go +++ b/api/server/router/container/backend.go @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/pkg/archive" ) @@ -64,7 +65,7 @@ type attachBackend interface { // systemBackend includes functions to implement to provide system wide containers functionality type systemBackend interface { - ContainersPrune(config *types.ContainersPruneConfig) (*types.ContainersPruneReport, error) + ContainersPrune(pruneFilters filters.Args) (*types.ContainersPruneReport, error) } // Backend is all the methods that need to be implemented to provide container specific functionality. diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go index 18bc50bcac..9c9bc0f8c3 100644 --- a/api/server/router/container/container_routes.go +++ b/api/server/router/container/container_routes.go @@ -541,16 +541,12 @@ func (s *containerRouter) postContainersPrune(ctx context.Context, w http.Respon return err } - if err := httputils.CheckForJSON(r); err != nil { + pruneFilters, err := filters.FromParam(r.Form.Get("filters")) + if err != nil { return err } - var cfg types.ContainersPruneConfig - if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { - return err - } - - pruneReport, err := s.backend.ContainersPrune(&cfg) + pruneReport, err := s.backend.ContainersPrune(pruneFilters) if err != nil { return err } diff --git a/api/server/router/image/backend.go b/api/server/router/image/backend.go index 5209d7e879..19a67a5ed0 100644 --- a/api/server/router/image/backend.go +++ b/api/server/router/image/backend.go @@ -29,7 +29,7 @@ type imageBackend interface { Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) LookupImage(name string) (*types.ImageInspect, error) TagImage(imageName, repository, tag string) error - ImagesPrune(config *types.ImagesPruneConfig) (*types.ImagesPruneReport, error) + ImagesPrune(pruneFilters filters.Args) (*types.ImagesPruneReport, error) } type importExportBackend interface { diff --git a/api/server/router/image/image_routes.go b/api/server/router/image/image_routes.go index 2b12503ddc..69403652a0 100644 --- a/api/server/router/image/image_routes.go +++ b/api/server/router/image/image_routes.go @@ -331,16 +331,12 @@ func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter return err } - if err := httputils.CheckForJSON(r); err != nil { + pruneFilters, err := filters.FromParam(r.Form.Get("filters")) + if err != nil { return err } - var cfg types.ImagesPruneConfig - if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { - return err - } - - pruneReport, err := s.backend.ImagesPrune(&cfg) + pruneReport, err := s.backend.ImagesPrune(pruneFilters) if err != nil { return err } diff --git a/api/server/router/network/backend.go b/api/server/router/network/backend.go index cf82398c93..0d1dfb0123 100644 --- a/api/server/router/network/backend.go +++ b/api/server/router/network/backend.go @@ -2,6 +2,7 @@ package network import ( "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/network" "github.com/docker/libnetwork" ) @@ -17,5 +18,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) + NetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) } diff --git a/api/server/router/network/network_routes.go b/api/server/router/network/network_routes.go index 39b45e58bc..5b7b9738eb 100644 --- a/api/server/router/network/network_routes.go +++ b/api/server/router/network/network_routes.go @@ -297,16 +297,7 @@ func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWr 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) + pruneReport, err := n.backend.NetworksPrune(filters.Args{}) if err != nil { return err } diff --git a/api/server/router/volume/backend.go b/api/server/router/volume/backend.go index 3a7608e0bd..180c06e5d3 100644 --- a/api/server/router/volume/backend.go +++ b/api/server/router/volume/backend.go @@ -3,6 +3,7 @@ package volume import ( // TODO return types need to be refactored into pkg "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" ) // Backend is the methods that need to be implemented to provide @@ -12,5 +13,5 @@ type Backend interface { VolumeInspect(name string) (*types.Volume, error) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) VolumeRm(name string, force bool) error - VolumesPrune(config *types.VolumesPruneConfig) (*types.VolumesPruneReport, error) + VolumesPrune(pruneFilters filters.Args) (*types.VolumesPruneReport, error) } diff --git a/api/server/router/volume/volume_routes.go b/api/server/router/volume/volume_routes.go index e0398817c3..cfd4618a4d 100644 --- a/api/server/router/volume/volume_routes.go +++ b/api/server/router/volume/volume_routes.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/docker/docker/api/server/httputils" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" volumetypes "github.com/docker/docker/api/types/volume" "golang.org/x/net/context" ) @@ -72,16 +72,7 @@ func (v *volumeRouter) postVolumesPrune(ctx context.Context, w http.ResponseWrit return err } - if err := httputils.CheckForJSON(r); err != nil { - return err - } - - var cfg types.VolumesPruneConfig - if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { - return err - } - - pruneReport, err := v.backend.VolumesPrune(&cfg) + pruneReport, err := v.backend.VolumesPrune(filters.Args{}) if err != nil { return err } diff --git a/api/swagger.yaml b/api/swagger.yaml index f3f0f4b66e..8449707df8 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -4186,11 +4186,17 @@ paths: /containers/prune: post: summary: "Delete stopped containers" - consumes: - - "application/json" produces: - "application/json" operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" responses: 200: description: "No error" @@ -4848,21 +4854,20 @@ paths: /images/prune: post: summary: "Delete unused images" - consumes: - - "application/json" produces: - "application/json" operationId: "ImagePrune" parameters: - - name: "body" - in: "body" - schema: - type: "object" - properties: - DanglingOnly: - description: "Only delete unused *and* untagged images" - type: "boolean" - default: false + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + type: "string" responses: 200: description: "No error" @@ -5944,11 +5949,17 @@ paths: /volumes/prune: post: summary: "Delete unused volumes" - consumes: - - "application/json" produces: - "application/json" operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" responses: 200: description: "No error" @@ -6286,6 +6297,14 @@ paths: produces: - "application/json" operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" responses: 200: description: "No error" diff --git a/api/types/types.go b/api/types/types.go index 4a96ec556a..a82c3e88ef 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -509,27 +509,6 @@ type DiskUsage struct { Volumes []*Volume } -// ImagesPruneConfig contains the configuration for Engine API: -// POST "/images/prune" -type ImagesPruneConfig struct { - DanglingOnly bool -} - -// ContainersPruneConfig contains the configuration for Engine API: -// POST "/images/prune" -type ContainersPruneConfig struct { -} - -// VolumesPruneConfig contains the configuration for Engine API: -// POST "/images/prune" -type VolumesPruneConfig struct { -} - -// NetworksPruneConfig contains the configuration for Engine API: -// POST "/networks/prune" -type NetworksPruneConfig struct { -} - // ContainersPruneReport contains the response for Engine API: // POST "/containers/prune" type ContainersPruneReport struct { diff --git a/cli/command/container/prune.go b/cli/command/container/prune.go index ec6b0e3147..064f4c08e0 100644 --- a/cli/command/container/prune.go +++ b/cli/command/container/prune.go @@ -5,7 +5,7 @@ import ( "golang.org/x/net/context" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" units "github.com/docker/go-units" @@ -52,7 +52,7 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed u return } - report, err := dockerCli.Client().ContainersPrune(context.Background(), types.ContainersPruneConfig{}) + report, err := dockerCli.Client().ContainersPrune(context.Background(), filters.Args{}) if err != nil { return } diff --git a/cli/command/image/prune.go b/cli/command/image/prune.go index ea84cda877..82c28fcf49 100644 --- a/cli/command/image/prune.go +++ b/cli/command/image/prune.go @@ -5,7 +5,7 @@ import ( "golang.org/x/net/context" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" units "github.com/docker/go-units" @@ -54,6 +54,9 @@ Are you sure you want to continue?` ) func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) { + pruneFilters := filters.NewArgs() + pruneFilters.Add("dangling", fmt.Sprintf("%v", !opts.all)) + warning := danglingWarning if opts.all { warning = allImageWarning @@ -62,9 +65,7 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed u return } - report, err := dockerCli.Client().ImagesPrune(context.Background(), types.ImagesPruneConfig{ - DanglingOnly: !opts.all, - }) + report, err := dockerCli.Client().ImagesPrune(context.Background(), pruneFilters) if err != nil { return } diff --git a/cli/command/network/prune.go b/cli/command/network/prune.go index f2f8cc20c4..9f1979e6b5 100644 --- a/cli/command/network/prune.go +++ b/cli/command/network/prune.go @@ -5,7 +5,7 @@ import ( "golang.org/x/net/context" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/spf13/cobra" @@ -50,7 +50,7 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (output string, e return } - report, err := dockerCli.Client().NetworksPrune(context.Background(), types.NetworksPruneConfig{}) + report, err := dockerCli.Client().NetworksPrune(context.Background(), filters.Args{}) if err != nil { return } diff --git a/cli/command/volume/prune.go b/cli/command/volume/prune.go index ac9c94451a..405fbeb295 100644 --- a/cli/command/volume/prune.go +++ b/cli/command/volume/prune.go @@ -5,7 +5,7 @@ import ( "golang.org/x/net/context" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" units "github.com/docker/go-units" @@ -52,7 +52,7 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed u return } - report, err := dockerCli.Client().VolumesPrune(context.Background(), types.VolumesPruneConfig{}) + report, err := dockerCli.Client().VolumesPrune(context.Background(), filters.Args{}) if err != nil { return } diff --git a/client/container_prune.go b/client/container_prune.go index 3eabe71a7f..b582170867 100644 --- a/client/container_prune.go +++ b/client/container_prune.go @@ -5,18 +5,24 @@ import ( "fmt" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "golang.org/x/net/context" ) // ContainersPrune requests the daemon to delete unused data -func (cli *Client) ContainersPrune(ctx context.Context, cfg types.ContainersPruneConfig) (types.ContainersPruneReport, error) { +func (cli *Client) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) { var report types.ContainersPruneReport if err := cli.NewVersionError("1.25", "container prune"); err != nil { return report, err } - serverResp, err := cli.post(ctx, "/containers/prune", nil, cfg, nil) + query, err := getFiltersQuery(pruneFilters) + if err != nil { + return report, err + } + + serverResp, err := cli.post(ctx, "/containers/prune", query, nil, nil) if err != nil { return report, err } diff --git a/client/image_prune.go b/client/image_prune.go index d5e69d5b19..5ef98b7f02 100644 --- a/client/image_prune.go +++ b/client/image_prune.go @@ -5,18 +5,24 @@ import ( "fmt" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "golang.org/x/net/context" ) // ImagesPrune requests the daemon to delete unused data -func (cli *Client) ImagesPrune(ctx context.Context, cfg types.ImagesPruneConfig) (types.ImagesPruneReport, error) { +func (cli *Client) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (types.ImagesPruneReport, error) { var report types.ImagesPruneReport if err := cli.NewVersionError("1.25", "image prune"); err != nil { return report, err } - serverResp, err := cli.post(ctx, "/images/prune", nil, cfg, nil) + query, err := getFiltersQuery(pruneFilters) + if err != nil { + return report, err + } + + serverResp, err := cli.post(ctx, "/images/prune", query, nil, nil) if err != nil { return report, err } diff --git a/client/interface.go b/client/interface.go index 0d722d9075..6319f34f1e 100644 --- a/client/interface.go +++ b/client/interface.go @@ -64,7 +64,7 @@ type ContainerAPIClient interface { ContainerWait(ctx context.Context, container string) (int64, error) CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error - ContainersPrune(ctx context.Context, cfg types.ContainersPruneConfig) (types.ContainersPruneReport, error) + ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) } // ImageAPIClient defines API client methods for the images @@ -82,7 +82,7 @@ type ImageAPIClient interface { ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error) ImageSave(ctx context.Context, images []string) (io.ReadCloser, error) ImageTag(ctx context.Context, image, ref string) error - ImagesPrune(ctx context.Context, cfg types.ImagesPruneConfig) (types.ImagesPruneReport, error) + ImagesPrune(ctx context.Context, pruneFilter filters.Args) (types.ImagesPruneReport, error) } // NetworkAPIClient defines API client methods for the networks @@ -94,7 +94,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) + NetworksPrune(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error) } // NodeAPIClient defines API client methods for the nodes @@ -157,7 +157,7 @@ type VolumeAPIClient interface { VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error) VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumesListOKBody, error) VolumeRemove(ctx context.Context, volumeID string, force bool) error - VolumesPrune(ctx context.Context, cfg types.VolumesPruneConfig) (types.VolumesPruneReport, error) + VolumesPrune(ctx context.Context, pruneFilter filters.Args) (types.VolumesPruneReport, error) } // SecretAPIClient defines API client methods for secrets diff --git a/client/network_prune.go b/client/network_prune.go index 01185f2e02..7352a7f0c5 100644 --- a/client/network_prune.go +++ b/client/network_prune.go @@ -5,14 +5,24 @@ import ( "fmt" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "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) { +func (cli *Client) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (types.NetworksPruneReport, error) { var report types.NetworksPruneReport - serverResp, err := cli.post(ctx, "/networks/prune", nil, cfg, nil) + if err := cli.NewVersionError("1.25", "network prune"); err != nil { + return report, err + } + + query, err := getFiltersQuery(pruneFilters) + if err != nil { + return report, err + } + + serverResp, err := cli.post(ctx, "/networks/prune", query, nil, nil) if err != nil { return report, err } diff --git a/client/utils.go b/client/utils.go index 03bf4c82fa..23d520ecb8 100644 --- a/client/utils.go +++ b/client/utils.go @@ -1,6 +1,10 @@ package client -import "regexp" +import ( + "github.com/docker/docker/api/types/filters" + "net/url" + "regexp" +) var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`) @@ -13,3 +17,17 @@ func getDockerOS(serverHeader string) string { } return osType } + +// getFiltersQuery returns a url query with "filters" query term, based on the +// filters provided. +func getFiltersQuery(f filters.Args) (url.Values, error) { + query := url.Values{} + if f.Len() > 0 { + filterJSON, err := filters.ToParam(f) + if err != nil { + return query, err + } + query.Set("filters", filterJSON) + } + return query, nil +} diff --git a/client/volume_prune.go b/client/volume_prune.go index ea4e234a30..a07e4ce637 100644 --- a/client/volume_prune.go +++ b/client/volume_prune.go @@ -5,18 +5,24 @@ import ( "fmt" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "golang.org/x/net/context" ) // VolumesPrune requests the daemon to delete unused data -func (cli *Client) VolumesPrune(ctx context.Context, cfg types.VolumesPruneConfig) (types.VolumesPruneReport, error) { +func (cli *Client) VolumesPrune(ctx context.Context, pruneFilters filters.Args) (types.VolumesPruneReport, error) { var report types.VolumesPruneReport if err := cli.NewVersionError("1.25", "volume prune"); err != nil { return report, err } - serverResp, err := cli.post(ctx, "/volumes/prune", nil, cfg, nil) + query, err := getFiltersQuery(pruneFilters) + if err != nil { + return report, err + } + + serverResp, err := cli.post(ctx, "/volumes/prune", query, nil, nil) if err != nil { return report, err } diff --git a/daemon/prune.go b/daemon/prune.go index 953a568d91..a693beb4e1 100644 --- a/daemon/prune.go +++ b/daemon/prune.go @@ -1,11 +1,13 @@ package daemon import ( + "fmt" "regexp" "github.com/Sirupsen/logrus" "github.com/docker/distribution/digest" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/directory" @@ -16,7 +18,7 @@ import ( ) // ContainersPrune removes unused containers -func (daemon *Daemon) ContainersPrune(config *types.ContainersPruneConfig) (*types.ContainersPruneReport, error) { +func (daemon *Daemon) ContainersPrune(pruneFilters filters.Args) (*types.ContainersPruneReport, error) { rep := &types.ContainersPruneReport{} allContainers := daemon.List() @@ -40,7 +42,7 @@ func (daemon *Daemon) ContainersPrune(config *types.ContainersPruneConfig) (*typ } // VolumesPrune removes unused local volumes -func (daemon *Daemon) VolumesPrune(config *types.VolumesPruneConfig) (*types.VolumesPruneReport, error) { +func (daemon *Daemon) VolumesPrune(pruneFilters filters.Args) (*types.VolumesPruneReport, error) { rep := &types.VolumesPruneReport{} pruneVols := func(v volume.Volume) error { @@ -70,11 +72,20 @@ func (daemon *Daemon) VolumesPrune(config *types.VolumesPruneConfig) (*types.Vol } // ImagesPrune removes unused images -func (daemon *Daemon) ImagesPrune(config *types.ImagesPruneConfig) (*types.ImagesPruneReport, error) { +func (daemon *Daemon) ImagesPrune(pruneFilters filters.Args) (*types.ImagesPruneReport, error) { rep := &types.ImagesPruneReport{} + danglingOnly := true + if pruneFilters.Include("dangling") { + if pruneFilters.ExactMatch("dangling", "false") || pruneFilters.ExactMatch("dangling", "0") { + danglingOnly = false + } else if !pruneFilters.ExactMatch("dangling", "true") && !pruneFilters.ExactMatch("dangling", "1") { + return nil, fmt.Errorf("Invalid filter 'dangling=%s'", pruneFilters.Get("dangling")) + } + } + var allImages map[image.ID]*image.Image - if config.DanglingOnly { + if danglingOnly { allImages = daemon.imageStore.Heads() } else { allImages = daemon.imageStore.Map() @@ -106,7 +117,7 @@ func (daemon *Daemon) ImagesPrune(config *types.ImagesPruneConfig) (*types.Image deletedImages := []types.ImageDelete{} refs := daemon.referenceStore.References(dgst) if len(refs) > 0 { - if config.DanglingOnly { + if danglingOnly { // Not a dangling image continue } @@ -156,7 +167,7 @@ func (daemon *Daemon) ImagesPrune(config *types.ImagesPruneConfig) (*types.Image } // localNetworksPrune removes unused local networks -func (daemon *Daemon) localNetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) { +func (daemon *Daemon) localNetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) { rep := &types.NetworksPruneReport{} var err error // When the function returns true, the walk will stop. @@ -177,7 +188,7 @@ func (daemon *Daemon) localNetworksPrune(config *types.NetworksPruneConfig) (*ty } // clusterNetworksPrune removes unused cluster networks -func (daemon *Daemon) clusterNetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) { +func (daemon *Daemon) clusterNetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) { rep := &types.NetworksPruneReport{} cluster := daemon.GetCluster() networks, err := cluster.GetNetworks() @@ -207,15 +218,15 @@ func (daemon *Daemon) clusterNetworksPrune(config *types.NetworksPruneConfig) (* } // NetworksPrune removes unused networks -func (daemon *Daemon) NetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) { +func (daemon *Daemon) NetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) { rep := &types.NetworksPruneReport{} - clusterRep, err := daemon.clusterNetworksPrune(config) + clusterRep, err := daemon.clusterNetworksPrune(pruneFilters) 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) + localRep, err := daemon.localNetworksPrune(pruneFilters) if err != nil { logrus.Warnf("could not remove local networks: %v", err) } else { diff --git a/integration-cli/docker_cli_prune_unix_test.go b/integration-cli/docker_cli_prune_unix_test.go index 5585cab302..dabbc72081 100644 --- a/integration-cli/docker_cli_prune_unix_test.go +++ b/integration-cli/docker_cli_prune_unix_test.go @@ -59,3 +59,33 @@ func (s *DockerSwarmSuite) TestPruneNetwork(c *check.C) { waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 0) pruneNetworkAndVerify(c, d, []string{}, []string{"n1", "n3"}) } + +func (s *DockerDaemonSuite) TestPruneImageDangling(c *check.C) { + c.Assert(s.d.StartWithBusybox(), checker.IsNil) + + out, _, err := s.d.buildImageWithOut("test", + `FROM busybox + LABEL foo=bar`, true, "-q") + c.Assert(err, checker.IsNil) + id := strings.TrimSpace(out) + + out, err = s.d.Cmd("images", "-q", "--no-trunc") + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Contains, id) + + out, err = s.d.Cmd("image", "prune", "--force") + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id) + + out, err = s.d.Cmd("images", "-q", "--no-trunc") + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Contains, id) + + out, err = s.d.Cmd("image", "prune", "--force", "--all") + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Contains, id) + + out, err = s.d.Cmd("images", "-q", "--no-trunc") + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id) +}