diff --git a/api/server/router/container/backend.go b/api/server/router/container/backend.go index f0624eef83..eea44d8b7a 100644 --- a/api/server/router/container/backend.go +++ b/api/server/router/container/backend.go @@ -49,7 +49,7 @@ type stateBackend interface { // monitorBackend includes functions to implement to provide containers monitoring functionality. type monitorBackend interface { ContainerChanges(name string) ([]archive.Change, error) - ContainerInspect(name string, size bool, version string) (interface{}, error) + ContainerInspect(ctx context.Context, name string, size bool, version string) (interface{}, error) ContainerLogs(ctx context.Context, name string, config *types.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error) ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error ContainerTop(name string, psArgs string) (*container.ContainerTopOKBody, error) diff --git a/api/server/router/container/inspect.go b/api/server/router/container/inspect.go index 5c78d15bc9..c905c969b8 100644 --- a/api/server/router/container/inspect.go +++ b/api/server/router/container/inspect.go @@ -12,7 +12,7 @@ func (s *containerRouter) getContainersByName(ctx context.Context, w http.Respon displaySize := httputils.BoolValue(r, "size") version := httputils.VersionFromContext(ctx) - json, err := s.backend.ContainerInspect(vars["name"], displaySize, version) + json, err := s.backend.ContainerInspect(ctx, vars["name"], displaySize, version) if err != nil { return err } diff --git a/daemon/cluster/executor/backend.go b/daemon/cluster/executor/backend.go index 5022979715..dd3f513364 100644 --- a/daemon/cluster/executor/backend.go +++ b/daemon/cluster/executor/backend.go @@ -45,7 +45,7 @@ type Backend interface { ActivateContainerServiceBinding(containerName string) error DeactivateContainerServiceBinding(containerName string) error UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error - ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error) + ContainerInspectCurrent(ctx context.Context, name string, size bool) (*types.ContainerJSON, error) ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error) ContainerRm(name string, config *types.ContainerRmConfig) error ContainerKill(name string, sig string) error diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index 1988baa4d9..28fcaaeb7f 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/daemon/cluster/executor/container/adapter.go @@ -361,7 +361,7 @@ func (c *containerAdapter) start(ctx context.Context) error { } func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) { - cs, err := c.backend.ContainerInspectCurrent(c.container.name(), false) + cs, err := c.backend.ContainerInspectCurrent(ctx, c.container.name(), false) if ctx.Err() != nil { return types.ContainerJSON{}, ctx.Err() } diff --git a/daemon/containerd/service.go b/daemon/containerd/service.go index 0bf81a26c1..7a768e681b 100644 --- a/daemon/containerd/service.go +++ b/daemon/containerd/service.go @@ -2,8 +2,10 @@ package containerd import ( "context" + "encoding/json" "github.com/containerd/containerd" + "github.com/containerd/containerd/content" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/remotes/docker" "github.com/containerd/containerd/snapshots" @@ -13,6 +15,9 @@ import ( "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/registry" + "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/identity" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -139,6 +144,56 @@ func (i *ImageService) GetLayerFolders(img *image.Image, rwLayer layer.RWLayer) } // GetContainerLayerSize returns the real size & virtual size of the container. -func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) { - panic("not implemented") +func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error) { + ctr := i.containers.Get(containerID) + if ctr == nil { + return 0, 0, nil + } + cs := i.client.ContentStore() + + imageManifestBytes, err := content.ReadBlob(ctx, cs, *ctr.ImageManifest) + if err != nil { + return 0, 0, err + } + + var manifest ocispec.Manifest + if err := json.Unmarshal(imageManifestBytes, &manifest); err != nil { + return 0, 0, err + } + + imageConfigBytes, err := content.ReadBlob(ctx, cs, manifest.Config) + if err != nil { + return 0, 0, err + } + var img ocispec.Image + if err := json.Unmarshal(imageConfigBytes, &img); err != nil { + return 0, 0, err + } + + snapshotter := i.client.SnapshotService(i.snapshotter) + usage, err := snapshotter.Usage(ctx, containerID) + if err != nil { + return 0, 0, err + } + + sizeCache := make(map[digest.Digest]int64) + snapshotSizeFn := func(d digest.Digest) (int64, error) { + if s, ok := sizeCache[d]; ok { + return s, nil + } + u, err := snapshotter.Usage(ctx, d.String()) + if err != nil { + return 0, err + } + sizeCache[d] = u.Size + return u.Size, nil + } + + chainIDs := identity.ChainIDs(img.RootFS.DiffIDs) + virtualSize, err := computeVirtualSize(chainIDs, snapshotSizeFn) + if err != nil { + return 0, 0, err + } + + return usage.Size, usage.Size + virtualSize, nil } diff --git a/daemon/image_service.go b/daemon/image_service.go index 23d50474c7..005c17736b 100644 --- a/daemon/image_service.go +++ b/daemon/image_service.go @@ -57,7 +57,7 @@ type ImageService interface { GetLayerMountID(cid string) (string, error) ReleaseLayer(rwlayer layer.RWLayer) error LayerDiskUsage(ctx context.Context) (int64, error) - GetContainerLayerSize(containerID string) (int64, int64) + GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error) Mount(ctx context.Context, container *container.Container) error Unmount(ctx context.Context, container *container.Container) error diff --git a/daemon/images/image_unix.go b/daemon/images/image_unix.go index aa9a4a01e4..dce61a5a30 100644 --- a/daemon/images/image_unix.go +++ b/daemon/images/image_unix.go @@ -4,6 +4,8 @@ package images // import "github.com/docker/docker/daemon/images" import ( + "context" + "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/sirupsen/logrus" @@ -16,7 +18,7 @@ func (i *ImageService) GetLayerFolders(img *image.Image, rwLayer layer.RWLayer) } // GetContainerLayerSize returns the real size & virtual size of the container. -func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) { +func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error) { var ( sizeRw, sizeRootfs int64 err error @@ -27,7 +29,7 @@ func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) rwlayer, err := i.layerStore.GetRWLayer(containerID) if err != nil { logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err) - return sizeRw, sizeRootfs + return sizeRw, sizeRootfs, nil } defer i.layerStore.ReleaseRWLayer(rwlayer) @@ -46,5 +48,5 @@ func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) sizeRootfs += sizeRw } } - return sizeRw, sizeRootfs + return sizeRw, sizeRootfs, nil } diff --git a/daemon/images/image_windows.go b/daemon/images/image_windows.go index 035d7b7139..d69674cbea 100644 --- a/daemon/images/image_windows.go +++ b/daemon/images/image_windows.go @@ -1,6 +1,8 @@ package images import ( + "context" + "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/system" @@ -8,9 +10,9 @@ import ( ) // GetContainerLayerSize returns real size & virtual size -func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) { +func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error) { // TODO Windows - return 0, 0 + return 0, 0, nil } // GetLayerFolders returns the layer folders from an image RootFS diff --git a/daemon/inspect.go b/daemon/inspect.go index 3abc7f9f1d..c3cd632810 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -1,6 +1,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( + "context" "errors" "fmt" "time" @@ -19,19 +20,19 @@ import ( // ContainerInspect returns low-level information about a // container. Returns an error if the container cannot be found, or if // there is an error getting the data. -func (daemon *Daemon) ContainerInspect(name string, size bool, version string) (interface{}, error) { +func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, size bool, version string) (interface{}, error) { switch { case versions.LessThan(version, "1.20"): - return daemon.containerInspectPre120(name) + return daemon.containerInspectPre120(ctx, name) case versions.Equal(version, "1.20"): return daemon.containerInspect120(name) } - return daemon.ContainerInspectCurrent(name, size) + return daemon.ContainerInspectCurrent(ctx, name, size) } // ContainerInspectCurrent returns low-level information about a // container in a most recent api version. -func (daemon *Daemon) ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error) { +func (daemon *Daemon) ContainerInspectCurrent(ctx context.Context, name string, size bool) (*types.ContainerJSON, error) { ctr, err := daemon.GetContainer(name) if err != nil { return nil, err @@ -78,7 +79,10 @@ func (daemon *Daemon) ContainerInspectCurrent(name string, size bool) (*types.Co ctr.Unlock() if size { - sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(base.ID) + sizeRw, sizeRootFs, err := daemon.imageService.GetContainerLayerSize(ctx, base.ID) + if err != nil { + return nil, err + } base.SizeRw = &sizeRw base.SizeRootFs = &sizeRootFs } diff --git a/daemon/inspect_linux.go b/daemon/inspect_linux.go index 1b38ec60c9..45ae0f7230 100644 --- a/daemon/inspect_linux.go +++ b/daemon/inspect_linux.go @@ -1,6 +1,8 @@ package daemon // import "github.com/docker/docker/daemon" import ( + "context" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/versions/v1p19" @@ -18,7 +20,7 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON } // containerInspectPre120 gets containers for pre 1.20 APIs. -func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON, error) { +func (daemon *Daemon) containerInspectPre120(ctx context.Context, name string) (*v1p19.ContainerJSON, error) { ctr, err := daemon.GetContainer(name) if err != nil { return nil, err diff --git a/daemon/inspect_windows.go b/daemon/inspect_windows.go index 9b219d8b8c..c43112f853 100644 --- a/daemon/inspect_windows.go +++ b/daemon/inspect_windows.go @@ -1,6 +1,8 @@ package daemon // import "github.com/docker/docker/daemon" import ( + "context" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/container" @@ -12,8 +14,8 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON } // containerInspectPre120 get containers for pre 1.20 APIs. -func (daemon *Daemon) containerInspectPre120(name string) (*types.ContainerJSON, error) { - return daemon.ContainerInspectCurrent(name, false) +func (daemon *Daemon) containerInspectPre120(ctx context.Context, name string) (*types.ContainerJSON, error) { + return daemon.ContainerInspectCurrent(ctx, name, false) } func inspectExecProcessConfig(e *container.ExecConfig) *backend.ExecProcessConfig { diff --git a/daemon/list.go b/daemon/list.go index 1c97a4e7fd..6ebcb0c0ea 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -238,7 +238,10 @@ func (daemon *Daemon) reducePsContainer(ctx context.Context, container *containe // release lock because size calculation is slow if filter.Size { - sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(newC.ID) + sizeRw, sizeRootFs, err := daemon.imageService.GetContainerLayerSize(ctx, newC.ID) + if err != nil { + return nil, err + } newC.SizeRw = sizeRw newC.SizeRootFs = sizeRootFs } diff --git a/daemon/prune.go b/daemon/prune.go index e3ef4668be..159d60c1d5 100644 --- a/daemon/prune.go +++ b/daemon/prune.go @@ -73,9 +73,12 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters. if !matchLabels(pruneFilters, c.Config.Labels) { continue } - cSize, _ := daemon.imageService.GetContainerLayerSize(c.ID) + cSize, _, err := daemon.imageService.GetContainerLayerSize(ctx, c.ID) + if err != nil { + return nil, err + } // TODO: sets RmLink to true? - err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{}) + err = daemon.ContainerRm(c.ID, &types.ContainerRmConfig{}) if err != nil { logrus.Warnf("failed to prune container %s: %v", c.ID, err) continue