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:
commit
745795ef2e
22 changed files with 171 additions and 108 deletions
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue