dotcloud/docker

View on GitHub
daemon/disk_usage.go

Summary

Maintainability
A
40 mins
Test Coverage
package daemon // import "github.com/docker/docker/daemon"

import (
    "context"
    "fmt"

    "github.com/docker/docker/api/server/router/system"
    "github.com/docker/docker/api/types"
    "github.com/docker/docker/api/types/container"
    "github.com/docker/docker/api/types/filters"
    "github.com/docker/docker/api/types/image"
    "github.com/docker/docker/api/types/volume"
    "github.com/pkg/errors"
    "golang.org/x/sync/errgroup"
)

// containerDiskUsage obtains information about container data disk usage
// and makes sure that only one calculation is performed at the same time.
func (daemon *Daemon) containerDiskUsage(ctx context.Context) ([]*container.Summary, error) {
    res, _, err := daemon.usageContainers.Do(ctx, struct{}{}, func(ctx context.Context) ([]*container.Summary, error) {
        // Retrieve container list
        containers, err := daemon.Containers(ctx, &container.ListOptions{
            Size: true,
            All:  true,
        })
        if err != nil {
            return nil, fmt.Errorf("failed to retrieve container list: %v", err)
        }
        return containers, nil
    })
    return res, err
}

// imageDiskUsage obtains information about image data disk usage from image service
// and makes sure that only one calculation is performed at the same time.
func (daemon *Daemon) imageDiskUsage(ctx context.Context) ([]*image.Summary, error) {
    imgs, _, err := daemon.usageImages.Do(ctx, struct{}{}, func(ctx context.Context) ([]*image.Summary, error) {
        // Get all top images with extra attributes
        imgs, err := daemon.imageService.Images(ctx, image.ListOptions{
            Filters:        filters.NewArgs(),
            SharedSize:     true,
            ContainerCount: true,
        })
        if err != nil {
            return nil, errors.Wrap(err, "failed to retrieve image list")
        }
        return imgs, nil
    })

    return imgs, err
}

// localVolumesSize obtains information about volume disk usage from volumes service
// and makes sure that only one size calculation is performed at the same time.
func (daemon *Daemon) localVolumesSize(ctx context.Context) ([]*volume.Volume, error) {
    volumes, _, err := daemon.usageVolumes.Do(ctx, struct{}{}, func(ctx context.Context) ([]*volume.Volume, error) {
        volumes, err := daemon.volumes.LocalVolumesSize(ctx)
        if err != nil {
            return nil, err
        }
        return volumes, nil
    })
    return volumes, err
}

// layerDiskUsage obtains information about layer disk usage from image service
// and makes sure that only one size calculation is performed at the same time.
func (daemon *Daemon) layerDiskUsage(ctx context.Context) (int64, error) {
    usage, _, err := daemon.usageLayer.Do(ctx, struct{}{}, func(ctx context.Context) (int64, error) {
        usage, err := daemon.imageService.LayerDiskUsage(ctx)
        if err != nil {
            return 0, err
        }
        return usage, nil
    })
    return usage, err
}

// SystemDiskUsage returns information about the daemon data disk usage.
// Callers must not mutate contents of the returned fields.
func (daemon *Daemon) SystemDiskUsage(ctx context.Context, opts system.DiskUsageOptions) (*types.DiskUsage, error) {
    eg, ctx := errgroup.WithContext(ctx)

    var containers []*container.Summary
    if opts.Containers {
        eg.Go(func() error {
            var err error
            containers, err = daemon.containerDiskUsage(ctx)
            return err
        })
    }

    var (
        images     []*image.Summary
        layersSize int64
    )
    if opts.Images {
        eg.Go(func() error {
            var err error
            images, err = daemon.imageDiskUsage(ctx)
            return err
        })
        eg.Go(func() error {
            var err error
            layersSize, err = daemon.layerDiskUsage(ctx)
            return err
        })
    }

    var volumes []*volume.Volume
    if opts.Volumes {
        eg.Go(func() error {
            var err error
            volumes, err = daemon.localVolumesSize(ctx)
            return err
        })
    }

    if err := eg.Wait(); err != nil {
        return nil, err
    }
    return &types.DiskUsage{
        LayersSize: layersSize,
        Containers: containers,
        Volumes:    volumes,
        Images:     images,
    }, nil
}