mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Convert DanglingOnly to Filters for docker image prune
This fix convert DanglingOnly in ImagesPruneConfig to Filters,
so that it is possible to maintain API compatibility in the future.
Several integration tests have been added to cover changes.
This fix is related to 28497.
A follow up to this PR will be done once this PR is merged.
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
(cherry picked from commit a6be56b54e
)
Signed-off-by: Victor Vieux <vieux@docker.com>
This commit is contained in:
parent
4d92237de1
commit
d1d6357beb
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -4186,11 +4186,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"
|
||||||
|
@ -4848,21 +4854,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"
|
||||||
|
@ -5944,11 +5949,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"
|
||||||
|
@ -6286,6 +6297,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…
Add table
Reference in a new issue