dotcloud/docker

View on GitHub
daemon/containerd/service.go

Summary

Maintainability
A
0 mins
Test Coverage
package containerd

import (
    "context"
    "fmt"
    "sync/atomic"

    "github.com/containerd/containerd"
    "github.com/containerd/containerd/content"
    "github.com/containerd/containerd/images"
    "github.com/containerd/containerd/plugin"
    "github.com/containerd/containerd/remotes/docker"
    "github.com/containerd/containerd/snapshots"
    cerrdefs "github.com/containerd/errdefs"
    "github.com/containerd/log"
    "github.com/containerd/platforms"
    "github.com/distribution/reference"
    "github.com/docker/docker/container"
    daemonevents "github.com/docker/docker/daemon/events"
    dimages "github.com/docker/docker/daemon/images"
    "github.com/docker/docker/daemon/snapshotter"
    "github.com/docker/docker/errdefs"
    "github.com/docker/docker/layer"
    "github.com/docker/docker/pkg/idtools"
    "github.com/docker/docker/registry"
    "github.com/pkg/errors"
)

// ImageService implements daemon.ImageService
type ImageService struct {
    client              *containerd.Client
    images              images.Store
    content             content.Store
    containers          container.Store
    snapshotterServices map[string]snapshots.Snapshotter
    snapshotter         string
    registryHosts       docker.RegistryHosts
    registryService     registryResolver
    eventsService       *daemonevents.Events
    pruneRunning        atomic.Bool
    refCountMounter     snapshotter.Mounter
    idMapping           idtools.IdentityMapping

    // defaultPlatformOverride is used in tests to override the host platform.
    defaultPlatformOverride platforms.MatchComparer
}

type registryResolver interface {
    IsInsecureRegistry(host string) bool
    ResolveRepository(name reference.Named) (*registry.RepositoryInfo, error)
    LookupPullEndpoints(hostname string) ([]registry.APIEndpoint, error)
    LookupPushEndpoints(hostname string) ([]registry.APIEndpoint, error)
}

type ImageServiceConfig struct {
    Client          *containerd.Client
    Containers      container.Store
    Snapshotter     string
    RegistryHosts   docker.RegistryHosts
    Registry        registryResolver
    EventsService   *daemonevents.Events
    RefCountMounter snapshotter.Mounter
    IDMapping       idtools.IdentityMapping
}

// NewService creates a new ImageService.
func NewService(config ImageServiceConfig) *ImageService {
    return &ImageService{
        client:  config.Client,
        images:  config.Client.ImageService(),
        content: config.Client.ContentStore(),
        snapshotterServices: map[string]snapshots.Snapshotter{
            config.Snapshotter: config.Client.SnapshotService(config.Snapshotter),
        },
        containers:      config.Containers,
        snapshotter:     config.Snapshotter,
        registryHosts:   config.RegistryHosts,
        registryService: config.Registry,
        eventsService:   config.EventsService,
        refCountMounter: config.RefCountMounter,
        idMapping:       config.IDMapping,
    }
}

func (i *ImageService) snapshotterService(snapshotter string) snapshots.Snapshotter {
    s, ok := i.snapshotterServices[snapshotter]
    if !ok {
        s = i.client.SnapshotService(snapshotter)
        i.snapshotterServices[snapshotter] = s
    }

    return s
}

// DistributionServices return services controlling daemon image storage.
func (i *ImageService) DistributionServices() dimages.DistributionServices {
    return dimages.DistributionServices{}
}

// CountImages returns the number of images stored by ImageService
// called from info.go
func (i *ImageService) CountImages(ctx context.Context) int {
    imgs, err := i.client.ListImages(ctx)
    if err != nil {
        return 0
    }

    return len(imgs)
}

// CreateLayer creates a filesystem layer for a container.
// called from create.go
// TODO: accept an opt struct instead of container?
func (i *ImageService) CreateLayer(container *container.Container, initFunc layer.MountInit) (layer.RWLayer, error) {
    return nil, errdefs.NotImplemented(errdefs.NotImplemented(errors.New("not implemented")))
}

// LayerStoreStatus returns the status for each layer store
// called from info.go
func (i *ImageService) LayerStoreStatus() [][2]string {
    // TODO(thaJeztah) do we want to add more details about the driver here?
    return [][2]string{
        {"driver-type", string(plugin.SnapshotPlugin)},
    }
}

// GetLayerMountID returns the mount ID for a layer
// called from daemon.go Daemon.Shutdown(), and Daemon.Cleanup() (cleanup is actually containerCleanup)
// TODO: needs to be refactored to Unmount (see callers), or removed and replaced with GetLayerByID
func (i *ImageService) GetLayerMountID(cid string) (string, error) {
    return "", errdefs.NotImplemented(errors.New("not implemented"))
}

// Cleanup resources before the process is shutdown.
// called from daemon.go Daemon.Shutdown()
func (i *ImageService) Cleanup() error {
    return nil
}

// StorageDriver returns the name of the default storage-driver (snapshotter)
// used by the ImageService.
func (i *ImageService) StorageDriver() string {
    return i.snapshotter
}

// ReleaseLayer releases a layer allowing it to be removed
// called from delete.go Daemon.cleanupContainer(), and Daemon.containerExport()
func (i *ImageService) ReleaseLayer(rwlayer layer.RWLayer) error {
    return errdefs.NotImplemented(errors.New("not implemented"))
}

// LayerDiskUsage returns the number of bytes used by layer stores
// called from disk_usage.go
func (i *ImageService) LayerDiskUsage(ctx context.Context) (int64, error) {
    var allLayersSize int64
    // TODO(thaJeztah): do we need to take multiple snapshotters into account? See https://github.com/moby/moby/issues/45273
    snapshotter := i.client.SnapshotService(i.snapshotter)
    snapshotter.Walk(ctx, func(ctx context.Context, info snapshots.Info) error {
        usage, err := snapshotter.Usage(ctx, info.Name)
        if err != nil {
            return err
        }
        allLayersSize += usage.Size
        return nil
    })
    return allLayersSize, nil
}

// UpdateConfig values
//
// called from reload.go
func (i *ImageService) UpdateConfig(maxDownloads, maxUploads int) {
    log.G(context.TODO()).Warn("max downloads and uploads is not yet implemented with the containerd store")
}

// GetContainerLayerSize returns the real size & virtual size of the container.
func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error) {
    ctr := i.containers.Get(containerID)
    if ctr == nil {
        return 0, 0, nil
    }

    snapshotter := i.client.SnapshotService(ctr.Driver)
    rwLayerUsage, err := snapshotter.Usage(ctx, containerID)
    if err != nil {
        if cerrdefs.IsNotFound(err) {
            return 0, 0, errdefs.NotFound(fmt.Errorf("rw layer snapshot not found for container %s", containerID))
        }
        return 0, 0, errdefs.System(errors.Wrapf(err, "snapshotter.Usage failed for %s", containerID))
    }

    unpackedUsage, err := calculateSnapshotParentUsage(ctx, snapshotter, containerID)
    if err != nil {
        if cerrdefs.IsNotFound(err) {
            log.G(ctx).WithField("ctr", containerID).Warn("parent of container snapshot no longer present")
        } else {
            log.G(ctx).WithError(err).WithField("ctr", containerID).Warn("unexpected error when calculating usage of the parent snapshots")
        }
    }
    log.G(ctx).WithFields(log.Fields{
        "rwLayerUsage": rwLayerUsage.Size,
        "unpacked":     unpackedUsage.Size,
    }).Debug("GetContainerLayerSize")

    // TODO(thaJeztah): include content-store size for the image (similar to "GET /images/json")
    return rwLayerUsage.Size, rwLayerUsage.Size + unpackedUsage.Size, nil
}