Skip to content

Commit

Permalink
Merge pull request #26108 from mlaventure/data-mngt
Browse files Browse the repository at this point in the history
New Data Management commands
  • Loading branch information
Arnaud Porterie committed Oct 1, 2016
2 parents 1833842 + 913e5cb commit 86de7c0
Show file tree
Hide file tree
Showing 49 changed files with 1,962 additions and 13 deletions.
6 changes: 6 additions & 0 deletions api/server/router/container/backend.go
Expand Up @@ -62,11 +62,17 @@ type attachBackend interface {
ContainerAttach(name string, c *backend.ContainerAttachConfig) error
}

// systemBackend includes functions to implement to provide system wide containers functionality
type systemBackend interface {
ContainersPrune(config *types.ContainersPruneConfig) (*types.ContainersPruneReport, error)
}

// Backend is all the methods that need to be implemented to provide container specific functionality.
type Backend interface {
execBackend
copyBackend
stateBackend
monitorBackend
attachBackend
systemBackend
}
1 change: 1 addition & 0 deletions api/server/router/container/container.go
Expand Up @@ -68,6 +68,7 @@ func (r *containerRouter) initRoutes() {
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
router.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
router.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
router.NewPostRoute("/containers/prune", r.postContainersPrune),
// PUT
router.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
// DELETE
Expand Down
21 changes: 21 additions & 0 deletions api/server/router/container/container_routes.go
Expand Up @@ -524,3 +524,24 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
}
return err
}

func (s *containerRouter) postContainersPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}

if err := httputils.CheckForJSON(r); err != nil {
return err
}

var cfg types.ContainersPruneConfig
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
return err
}

pruneReport, err := s.backend.ContainersPrune(&cfg)
if err != nil {
return err
}
return httputils.WriteJSON(w, http.StatusOK, pruneReport)
}
3 changes: 2 additions & 1 deletion api/server/router/image/backend.go
Expand Up @@ -25,9 +25,10 @@ type containerBackend interface {
type imageBackend interface {
ImageDelete(imageRef string, force, prune bool) ([]types.ImageDelete, error)
ImageHistory(imageName string) ([]*types.ImageHistory, error)
Images(filterArgs string, filter string, all bool) ([]*types.Image, error)
Images(filterArgs string, filter string, all bool, withExtraAttrs bool) ([]*types.Image, error)
LookupImage(name string) (*types.ImageInspect, error)
TagImage(imageName, repository, tag string) error
ImagesPrune(config *types.ImagesPruneConfig) (*types.ImagesPruneReport, error)
}

type importExportBackend interface {
Expand Down
1 change: 1 addition & 0 deletions api/server/router/image/image.go
Expand Up @@ -43,6 +43,7 @@ func (r *imageRouter) initRoutes() {
router.Cancellable(router.NewPostRoute("/images/create", r.postImagesCreate)),
router.Cancellable(router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush)),
router.NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
router.NewPostRoute("/images/prune", r.postImagesPrune),
// DELETE
router.NewDeleteRoute("/images/{name:.*}", r.deleteImages),
}
Expand Down
23 changes: 22 additions & 1 deletion api/server/router/image/image_routes.go
Expand Up @@ -248,7 +248,7 @@ func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter,
}

// FIXME: The filter parameter could just be a match filter
images, err := s.backend.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
images, err := s.backend.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"), false)
if err != nil {
return err
}
Expand Down Expand Up @@ -314,3 +314,24 @@ func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter
}
return httputils.WriteJSON(w, http.StatusOK, query.Results)
}

func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}

if err := httputils.CheckForJSON(r); err != nil {
return err
}

var cfg types.ImagesPruneConfig
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
return err
}

pruneReport, err := s.backend.ImagesPrune(&cfg)
if err != nil {
return err
}
return httputils.WriteJSON(w, http.StatusOK, pruneReport)
}
1 change: 1 addition & 0 deletions api/server/router/system/backend.go
Expand Up @@ -14,6 +14,7 @@ import (
type Backend interface {
SystemInfo() (*types.Info, error)
SystemVersion() types.Version
SystemDiskUsage() (*types.DiskUsage, error)
SubscribeToEvents(since, until time.Time, ef filters.Args) ([]events.Message, chan interface{})
UnsubscribeFromEvents(chan interface{})
AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error)
Expand Down
1 change: 1 addition & 0 deletions api/server/router/system/system.go
Expand Up @@ -26,6 +26,7 @@ func NewRouter(b Backend, c *cluster.Cluster) router.Router {
router.Cancellable(router.NewGetRoute("/events", r.getEvents)),
router.NewGetRoute("/info", r.getInfo),
router.NewGetRoute("/version", r.getVersion),
router.NewGetRoute("/system/df", r.getDiskUsage),
router.NewPostRoute("/auth", r.postAuth),
}

Expand Down
9 changes: 9 additions & 0 deletions api/server/router/system/system_routes.go
Expand Up @@ -56,6 +56,15 @@ func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r
return httputils.WriteJSON(w, http.StatusOK, info)
}

func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
du, err := s.backend.SystemDiskUsage()
if err != nil {
return err
}

return httputils.WriteJSON(w, http.StatusOK, du)
}

func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions api/server/router/volume/backend.go
Expand Up @@ -12,4 +12,5 @@ type Backend interface {
VolumeInspect(name string) (*types.Volume, error)
VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
VolumeRm(name string, force bool) error
VolumesPrune(config *types.VolumesPruneConfig) (*types.VolumesPruneReport, error)
}
1 change: 1 addition & 0 deletions api/server/router/volume/volume.go
Expand Up @@ -29,6 +29,7 @@ func (r *volumeRouter) initRoutes() {
router.NewGetRoute("/volumes/{name:.*}", r.getVolumeByName),
// POST
router.NewPostRoute("/volumes/create", r.postVolumesCreate),
router.NewPostRoute("/volumes/prune", r.postVolumesPrune),
// DELETE
router.NewDeleteRoute("/volumes/{name:.*}", r.deleteVolumes),
}
Expand Down
21 changes: 21 additions & 0 deletions api/server/router/volume/volume_routes.go
Expand Up @@ -65,3 +65,24 @@ func (v *volumeRouter) deleteVolumes(ctx context.Context, w http.ResponseWriter,
w.WriteHeader(http.StatusNoContent)
return nil
}

func (v *volumeRouter) postVolumesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}

if err := httputils.CheckForJSON(r); err != nil {
return err
}

var cfg types.VolumesPruneConfig
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
return err
}

pruneReport, err := v.backend.VolumesPrune(&cfg)
if err != nil {
return err
}
return httputils.WriteJSON(w, http.StatusOK, pruneReport)
}
50 changes: 50 additions & 0 deletions api/types/types.go
Expand Up @@ -95,8 +95,10 @@ type Image struct {
RepoDigests []string
Created int64
Size int64
SharedSize int64
VirtualSize int64
Labels map[string]string
Containers int64
}

// GraphDriverData returns Image's graph driver config info
Expand Down Expand Up @@ -438,6 +440,8 @@ type Volume struct {
Status map[string]interface{} `json:",omitempty"` // Status provides low-level status information about the volume
Labels map[string]string // Labels is metadata specific to the volume
Scope string // Scope describes the level at which the volume exists (e.g. `global` for cluster-wide or `local` for machine level)
Size int64 // Size holds how much disk space is used by the (local driver only). Sets to -1 if not provided.
RefCount int // RefCount holds the number of containers having this volume attached to them. Sets to -1 if not provided.
}

// VolumesListResponse contains the response for the remote API:
Expand Down Expand Up @@ -526,3 +530,49 @@ type Runtime struct {
Path string `json:"path"`
Args []string `json:"runtimeArgs,omitempty"`
}

// DiskUsage contains response of Remote API:
// GET "/system/df"
type DiskUsage struct {
LayersSize int64
Images []*Image
Containers []*Container
Volumes []*Volume
}

// ImagesPruneConfig contains the configuration for Remote API:
// POST "/image/prune"
type ImagesPruneConfig struct {
DanglingOnly bool
}

// ContainersPruneConfig contains the configuration for Remote API:
// POST "/image/prune"
type ContainersPruneConfig struct {
}

// VolumesPruneConfig contains the configuration for Remote API:
// POST "/images/prune"
type VolumesPruneConfig struct {
}

// ContainersPruneReport contains the response for Remote API:
// POST "/containers/prune"
type ContainersPruneReport struct {
ContainersDeleted []string
SpaceReclaimed uint64
}

// VolumesPruneReport contains the response for Remote API:
// POST "/volumes/prune"
type VolumesPruneReport struct {
VolumesDeleted []string
SpaceReclaimed uint64
}

// ImagesPruneReport contains the response for Remote API:
// POST "/image/prune"
type ImagesPruneReport struct {
ImagesDeleted []ImageDelete
SpaceReclaimed uint64
}
1 change: 1 addition & 0 deletions cli/command/container/cmd.go
Expand Up @@ -44,6 +44,7 @@ func NewContainerCommand(dockerCli *command.DockerCli) *cobra.Command {
NewWaitCommand(dockerCli),
newListCommand(dockerCli),
newInspectCommand(dockerCli),
NewPruneCommand(dockerCli),
)
return cmd
}
74 changes: 74 additions & 0 deletions cli/command/container/prune.go
@@ -0,0 +1,74 @@
package container

import (
"fmt"

"golang.org/x/net/context"

"github.com/docker/docker/api/types"
"github.com/docker/docker/cli"
"github.com/docker/docker/cli/command"
units "github.com/docker/go-units"
"github.com/spf13/cobra"
)

type pruneOptions struct {
force bool
}

// NewPruneCommand returns a new cobra prune command for containers
func NewPruneCommand(dockerCli *command.DockerCli) *cobra.Command {
var opts pruneOptions

cmd := &cobra.Command{
Use: "prune",
Short: "Remove all stopped containers",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
spaceReclaimed, output, err := runPrune(dockerCli, opts)
if err != nil {
return err
}
if output != "" {
fmt.Fprintln(dockerCli.Out(), output)
}
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
return nil
},
}

flags := cmd.Flags()
flags.BoolVarP(&opts.force, "force", "f", false, "Do not prompt for confirmation")

return cmd
}

const warning = `WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] `

func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) {
if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
return
}

report, err := dockerCli.Client().ContainersPrune(context.Background(), types.ContainersPruneConfig{})
if err != nil {
return
}

if len(report.ContainersDeleted) > 0 {
output = "Deleted Containers:"
for _, id := range report.ContainersDeleted {
output += id + "\n"
}
spaceReclaimed = report.SpaceReclaimed
}

return
}

// RunPrune call the Container Prune API
// This returns the amount of space reclaimed and a detailed output string
func RunPrune(dockerCli *command.DockerCli) (uint64, string, error) {
return runPrune(dockerCli, pruneOptions{force: true})
}
3 changes: 1 addition & 2 deletions cli/command/container/stats.go
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/docker/docker/cli"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/command/formatter"
"github.com/docker/docker/cli/command/system"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -110,7 +109,7 @@ func runStats(dockerCli *command.DockerCli, opts *statsOptions) error {
// retrieving the list of running containers to avoid a race where we
// would "miss" a creation.
started := make(chan struct{})
eh := system.InitEventHandler()
eh := command.InitEventHandler()
eh.Handle("create", func(e events.Message) {
if opts.all {
s := formatter.NewContainerStats(e.ID[:12], daemonOSType)
Expand Down
@@ -1,4 +1,4 @@
package system
package command

import (
"sync"
Expand Down
14 changes: 14 additions & 0 deletions cli/command/formatter/container.go
Expand Up @@ -23,6 +23,7 @@ const (
statusHeader = "STATUS"
portsHeader = "PORTS"
mountsHeader = "MOUNTS"
localVolumes = "LOCAL VOLUMES"
)

// NewContainerFormat returns a Format for rendering using a Context
Expand Down Expand Up @@ -199,3 +200,16 @@ func (c *containerContext) Mounts() string {
}
return strings.Join(mounts, ",")
}

func (c *containerContext) LocalVolumes() string {
c.AddHeader(localVolumes)

count := 0
for _, m := range c.c.Mounts {
if m.Driver == "local" {
count++
}
}

return fmt.Sprintf("%d", count)
}

0 comments on commit 86de7c0

Please sign in to comment.