1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #28535 from yongtang/28497-prune-until

Convert DanglingOnly to Filters for `docker image prune`
This commit is contained in:
Vincent Demeester 2016-12-06 11:03:45 +01:00 committed by GitHub
commit 745795ef2e
22 changed files with 171 additions and 108 deletions

View file

@ -9,6 +9,7 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
) )
@ -64,7 +65,7 @@ type attachBackend interface {
// systemBackend includes functions to implement to provide system wide containers functionality // systemBackend includes functions to implement to provide system wide containers functionality
type systemBackend interface { 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. // Backend is all the methods that need to be implemented to provide container specific functionality.

View file

@ -541,16 +541,12 @@ func (s *containerRouter) postContainersPrune(ctx context.Context, w http.Respon
return err return err
} }
if err := httputils.CheckForJSON(r); err != nil { pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
if err != nil {
return err return err
} }
var cfg types.ContainersPruneConfig pruneReport, err := s.backend.ContainersPrune(pruneFilters)
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
return err
}
pruneReport, err := s.backend.ContainersPrune(&cfg)
if err != nil { if err != nil {
return err return err
} }

View file

@ -29,7 +29,7 @@ type imageBackend interface {
Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error)
LookupImage(name string) (*types.ImageInspect, error) LookupImage(name string) (*types.ImageInspect, error)
TagImage(imageName, repository, tag string) error TagImage(imageName, repository, tag string) error
ImagesPrune(config *types.ImagesPruneConfig) (*types.ImagesPruneReport, error) ImagesPrune(pruneFilters filters.Args) (*types.ImagesPruneReport, error)
} }
type importExportBackend interface { type importExportBackend interface {

View file

@ -331,16 +331,12 @@ func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter
return err return err
} }
if err := httputils.CheckForJSON(r); err != nil { pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
if err != nil {
return err return err
} }
var cfg types.ImagesPruneConfig pruneReport, err := s.backend.ImagesPrune(pruneFilters)
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
return err
}
pruneReport, err := s.backend.ImagesPrune(&cfg)
if err != nil { if err != nil {
return err return err
} }

View file

@ -2,6 +2,7 @@ package network
import ( import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/libnetwork" "github.com/docker/libnetwork"
) )
@ -17,5 +18,5 @@ type Backend interface {
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error
DeleteNetwork(name string) error DeleteNetwork(name string) error
NetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) NetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error)
} }

View file

@ -297,16 +297,7 @@ func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWr
return err return err
} }
if err := httputils.CheckForJSON(r); err != nil { pruneReport, err := n.backend.NetworksPrune(filters.Args{})
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 { if err != nil {
return err return err
} }

View file

@ -3,6 +3,7 @@ package volume
import ( import (
// TODO return types need to be refactored into pkg // TODO return types need to be refactored into pkg
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
) )
// Backend is the methods that need to be implemented to provide // Backend is the methods that need to be implemented to provide
@ -12,5 +13,5 @@ type Backend interface {
VolumeInspect(name string) (*types.Volume, error) VolumeInspect(name string) (*types.Volume, error)
VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
VolumeRm(name string, force bool) error VolumeRm(name string, force bool) error
VolumesPrune(config *types.VolumesPruneConfig) (*types.VolumesPruneReport, error) VolumesPrune(pruneFilters filters.Args) (*types.VolumesPruneReport, error)
} }

View file

@ -5,7 +5,7 @@ import (
"net/http" "net/http"
"github.com/docker/docker/api/server/httputils" "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" volumetypes "github.com/docker/docker/api/types/volume"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -72,16 +72,7 @@ func (v *volumeRouter) postVolumesPrune(ctx context.Context, w http.ResponseWrit
return err return err
} }
if err := httputils.CheckForJSON(r); err != nil { pruneReport, err := v.backend.VolumesPrune(filters.Args{})
return err
}
var cfg types.VolumesPruneConfig
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
return err
}
pruneReport, err := v.backend.VolumesPrune(&cfg)
if err != nil { if err != nil {
return err return err
} }

View file

@ -4187,11 +4187,17 @@ paths:
/containers/prune: /containers/prune:
post: post:
summary: "Delete stopped containers" summary: "Delete stopped containers"
consumes:
- "application/json"
produces: produces:
- "application/json" - "application/json"
operationId: "ContainerPrune" 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: responses:
200: 200:
description: "No error" description: "No error"
@ -4849,21 +4855,20 @@ paths:
/images/prune: /images/prune:
post: post:
summary: "Delete unused images" summary: "Delete unused images"
consumes:
- "application/json"
produces: produces:
- "application/json" - "application/json"
operationId: "ImagePrune" operationId: "ImagePrune"
parameters: parameters:
- name: "body" - name: "filters"
in: "body" in: "query"
schema: description: |
type: "object" Filters to process on the prune list, encoded as JSON (a `map[string][]string`).
properties:
DanglingOnly: Available filters:
description: "Only delete unused *and* untagged images" - `dangling=<boolean>` When set to `true` (or `1`), prune only
type: "boolean" unused *and* untagged images. When set to `false`
default: false (or `0`), all unused images are pruned.
type: "string"
responses: responses:
200: 200:
description: "No error" description: "No error"
@ -5945,11 +5950,17 @@ paths:
/volumes/prune: /volumes/prune:
post: post:
summary: "Delete unused volumes" summary: "Delete unused volumes"
consumes:
- "application/json"
produces: produces:
- "application/json" - "application/json"
operationId: "VolumePrune" 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: responses:
200: 200:
description: "No error" description: "No error"
@ -6287,6 +6298,14 @@ paths:
produces: produces:
- "application/json" - "application/json"
operationId: "NetworkPrune" 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: responses:
200: 200:
description: "No error" description: "No error"

View file

@ -509,27 +509,6 @@ type DiskUsage struct {
Volumes []*Volume 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: // ContainersPruneReport contains the response for Engine API:
// POST "/containers/prune" // POST "/containers/prune"
type ContainersPruneReport struct { type ContainersPruneReport struct {

View file

@ -5,7 +5,7 @@ import (
"golang.org/x/net/context" "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"
"github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command"
units "github.com/docker/go-units" units "github.com/docker/go-units"
@ -52,7 +52,7 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed u
return return
} }
report, err := dockerCli.Client().ContainersPrune(context.Background(), types.ContainersPruneConfig{}) report, err := dockerCli.Client().ContainersPrune(context.Background(), filters.Args{})
if err != nil { if err != nil {
return return
} }

View file

@ -5,7 +5,7 @@ import (
"golang.org/x/net/context" "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"
"github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command"
units "github.com/docker/go-units" 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) { 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 warning := danglingWarning
if opts.all { if opts.all {
warning = allImageWarning warning = allImageWarning
@ -62,9 +65,7 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed u
return return
} }
report, err := dockerCli.Client().ImagesPrune(context.Background(), types.ImagesPruneConfig{ report, err := dockerCli.Client().ImagesPrune(context.Background(), pruneFilters)
DanglingOnly: !opts.all,
})
if err != nil { if err != nil {
return return
} }

View file

@ -5,7 +5,7 @@ import (
"golang.org/x/net/context" "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"
"github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -50,7 +50,7 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (output string, e
return return
} }
report, err := dockerCli.Client().NetworksPrune(context.Background(), types.NetworksPruneConfig{}) report, err := dockerCli.Client().NetworksPrune(context.Background(), filters.Args{})
if err != nil { if err != nil {
return return
} }

View file

@ -5,7 +5,7 @@ import (
"golang.org/x/net/context" "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"
"github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command"
units "github.com/docker/go-units" units "github.com/docker/go-units"
@ -52,7 +52,7 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed u
return return
} }
report, err := dockerCli.Client().VolumesPrune(context.Background(), types.VolumesPruneConfig{}) report, err := dockerCli.Client().VolumesPrune(context.Background(), filters.Args{})
if err != nil { if err != nil {
return return
} }

View file

@ -5,18 +5,24 @@ import (
"fmt" "fmt"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// ContainersPrune requests the daemon to delete unused data // 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 var report types.ContainersPruneReport
if err := cli.NewVersionError("1.25", "container prune"); err != nil { if err := cli.NewVersionError("1.25", "container prune"); err != nil {
return report, err 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 { if err != nil {
return report, err return report, err
} }

View file

@ -5,18 +5,24 @@ import (
"fmt" "fmt"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// ImagesPrune requests the daemon to delete unused data // 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 var report types.ImagesPruneReport
if err := cli.NewVersionError("1.25", "image prune"); err != nil { if err := cli.NewVersionError("1.25", "image prune"); err != nil {
return report, err 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 { if err != nil {
return report, err return report, err
} }

View file

@ -64,7 +64,7 @@ type ContainerAPIClient interface {
ContainerWait(ctx context.Context, container string) (int64, error) ContainerWait(ctx context.Context, container string) (int64, error)
CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, 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 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 // 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) ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error)
ImageSave(ctx context.Context, images []string) (io.ReadCloser, error) ImageSave(ctx context.Context, images []string) (io.ReadCloser, error)
ImageTag(ctx context.Context, image, ref string) 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 // 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) NetworkInspectWithRaw(ctx context.Context, networkID string) (types.NetworkResource, []byte, error)
NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
NetworkRemove(ctx context.Context, networkID string) 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 // 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) VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error)
VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumesListOKBody, error) VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumesListOKBody, error)
VolumeRemove(ctx context.Context, volumeID string, force bool) 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 // SecretAPIClient defines API client methods for secrets

View file

@ -5,14 +5,24 @@ import (
"fmt" "fmt"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// NetworksPrune requests the daemon to delete unused networks // 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 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 { if err != nil {
return report, err return report, err
} }

View file

@ -1,6 +1,10 @@
package client package client
import "regexp" import (
"github.com/docker/docker/api/types/filters"
"net/url"
"regexp"
)
var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`) var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`)
@ -13,3 +17,17 @@ func getDockerOS(serverHeader string) string {
} }
return osType 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
}

View file

@ -5,18 +5,24 @@ import (
"fmt" "fmt"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// VolumesPrune requests the daemon to delete unused data // 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 var report types.VolumesPruneReport
if err := cli.NewVersionError("1.25", "volume prune"); err != nil { if err := cli.NewVersionError("1.25", "volume prune"); err != nil {
return report, err 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 { if err != nil {
return report, err return report, err
} }

View file

@ -1,11 +1,13 @@
package daemon package daemon
import ( import (
"fmt"
"regexp" "regexp"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/layer" "github.com/docker/docker/layer"
"github.com/docker/docker/pkg/directory" "github.com/docker/docker/pkg/directory"
@ -16,7 +18,7 @@ import (
) )
// ContainersPrune removes unused containers // 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{} rep := &types.ContainersPruneReport{}
allContainers := daemon.List() allContainers := daemon.List()
@ -40,7 +42,7 @@ func (daemon *Daemon) ContainersPrune(config *types.ContainersPruneConfig) (*typ
} }
// VolumesPrune removes unused local volumes // 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{} rep := &types.VolumesPruneReport{}
pruneVols := func(v volume.Volume) error { pruneVols := func(v volume.Volume) error {
@ -70,11 +72,20 @@ func (daemon *Daemon) VolumesPrune(config *types.VolumesPruneConfig) (*types.Vol
} }
// ImagesPrune removes unused images // 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{} 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 var allImages map[image.ID]*image.Image
if config.DanglingOnly { if danglingOnly {
allImages = daemon.imageStore.Heads() allImages = daemon.imageStore.Heads()
} else { } else {
allImages = daemon.imageStore.Map() allImages = daemon.imageStore.Map()
@ -106,7 +117,7 @@ func (daemon *Daemon) ImagesPrune(config *types.ImagesPruneConfig) (*types.Image
deletedImages := []types.ImageDelete{} deletedImages := []types.ImageDelete{}
refs := daemon.referenceStore.References(dgst) refs := daemon.referenceStore.References(dgst)
if len(refs) > 0 { if len(refs) > 0 {
if config.DanglingOnly { if danglingOnly {
// Not a dangling image // Not a dangling image
continue continue
} }
@ -156,7 +167,7 @@ func (daemon *Daemon) ImagesPrune(config *types.ImagesPruneConfig) (*types.Image
} }
// localNetworksPrune removes unused local networks // 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{} rep := &types.NetworksPruneReport{}
var err error var err error
// When the function returns true, the walk will stop. // 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 // 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{} rep := &types.NetworksPruneReport{}
cluster := daemon.GetCluster() cluster := daemon.GetCluster()
networks, err := cluster.GetNetworks() networks, err := cluster.GetNetworks()
@ -207,15 +218,15 @@ func (daemon *Daemon) clusterNetworksPrune(config *types.NetworksPruneConfig) (*
} }
// NetworksPrune removes unused networks // 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{} rep := &types.NetworksPruneReport{}
clusterRep, err := daemon.clusterNetworksPrune(config) clusterRep, err := daemon.clusterNetworksPrune(pruneFilters)
if err != nil { if err != nil {
logrus.Warnf("could not remove cluster networks: %v", err) logrus.Warnf("could not remove cluster networks: %v", err)
} else { } else {
rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...) rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...)
} }
localRep, err := daemon.localNetworksPrune(config) localRep, err := daemon.localNetworksPrune(pruneFilters)
if err != nil { if err != nil {
logrus.Warnf("could not remove local networks: %v", err) logrus.Warnf("could not remove local networks: %v", err)
} else { } else {

View file

@ -59,3 +59,33 @@ func (s *DockerSwarmSuite) TestPruneNetwork(c *check.C) {
waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 0) waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 0)
pruneNetworkAndVerify(c, d, []string{}, []string{"n1", "n3"}) 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)
}