2022-07-05 14:33:39 +00:00
package containerd
import (
"context"
2023-03-06 15:02:37 +00:00
"encoding/json"
2023-04-04 14:10:25 +00:00
"sync/atomic"
2022-07-05 14:33:39 +00:00
"github.com/containerd/containerd"
2023-03-06 15:02:37 +00:00
"github.com/containerd/containerd/content"
2023-06-23 00:33:17 +00:00
"github.com/containerd/containerd/log"
2022-08-22 16:36:03 +00:00
"github.com/containerd/containerd/plugin"
2022-09-12 08:40:45 +00:00
"github.com/containerd/containerd/remotes/docker"
2022-09-01 08:30:34 +00:00
"github.com/containerd/containerd/snapshots"
2023-04-13 12:19:51 +00:00
"github.com/docker/distribution/reference"
2023-04-25 13:10:55 +00:00
imagetypes "github.com/docker/docker/api/types/image"
2022-07-05 14:33:39 +00:00
"github.com/docker/docker/container"
2023-03-30 14:11:02 +00:00
daemonevents "github.com/docker/docker/daemon/events"
2022-07-05 14:33:39 +00:00
"github.com/docker/docker/daemon/images"
2023-05-15 15:42:37 +00:00
"github.com/docker/docker/daemon/snapshotter"
2022-08-10 13:22:32 +00:00
"github.com/docker/docker/errdefs"
2022-07-05 14:33:39 +00:00
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
2023-04-13 12:19:51 +00:00
"github.com/docker/docker/registry"
2023-03-06 15:02:37 +00:00
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/identity"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2022-09-01 08:30:34 +00:00
"github.com/pkg/errors"
2023-04-25 13:10:55 +00:00
"github.com/sirupsen/logrus"
2022-07-05 14:33:39 +00:00
)
// ImageService implements daemon.ImageService
type ImageService struct {
2022-09-01 15:03:10 +00:00
client * containerd . Client
2022-08-04 08:37:04 +00:00
containers container . Store
2022-09-01 15:03:10 +00:00
snapshotter string
2023-05-25 20:51:37 +00:00
registryHosts docker . RegistryHosts
2023-03-01 00:25:15 +00:00
registryService RegistryConfigProvider
2023-03-30 14:11:02 +00:00
eventsService * daemonevents . Events
2023-04-04 14:10:25 +00:00
pruneRunning atomic . Bool
2023-05-15 15:42:37 +00:00
refCountMounter snapshotter . Mounter
2022-09-12 08:40:45 +00:00
}
2023-03-01 00:25:15 +00:00
type RegistryConfigProvider interface {
IsInsecureRegistry ( host string ) bool
2023-04-13 12:19:51 +00:00
ResolveRepository ( name reference . Named ) ( * registry . RepositoryInfo , error )
2023-03-01 00:25:15 +00:00
}
2023-03-30 14:11:02 +00:00
type ImageServiceConfig struct {
2023-05-15 15:42:37 +00:00
Client * containerd . Client
Containers container . Store
Snapshotter string
RegistryHosts docker . RegistryHosts
Registry RegistryConfigProvider
EventsService * daemonevents . Events
RefCountMounter snapshotter . Mounter
2023-03-30 14:11:02 +00:00
}
2022-07-05 14:33:39 +00:00
// NewService creates a new ImageService.
2023-03-30 14:11:02 +00:00
func NewService ( config ImageServiceConfig ) * ImageService {
2022-07-05 14:33:39 +00:00
return & ImageService {
2023-03-30 14:11:02 +00:00
client : config . Client ,
containers : config . Containers ,
snapshotter : config . Snapshotter ,
2023-05-25 20:51:37 +00:00
registryHosts : config . RegistryHosts ,
2023-03-30 14:11:02 +00:00
registryService : config . Registry ,
eventsService : config . EventsService ,
2023-05-15 15:42:37 +00:00
refCountMounter : config . RefCountMounter ,
2022-07-05 14:33:39 +00:00
}
}
2022-07-18 10:51:49 +00:00
// DistributionServices return services controlling daemon image storage.
func ( i * ImageService ) DistributionServices ( ) images . DistributionServices {
return images . DistributionServices { }
2022-07-05 14:33:39 +00:00
}
2022-07-18 10:51:49 +00:00
// CountImages returns the number of images stored by ImageService
// called from info.go
func ( i * ImageService ) CountImages ( ) int {
imgs , err := i . client . ListImages ( context . TODO ( ) )
2022-07-05 14:33:39 +00:00
if err != nil {
2022-07-18 10:51:49 +00:00
return 0
2022-07-05 14:33:39 +00:00
}
2022-07-18 10:51:49 +00:00
return len ( imgs )
2022-07-05 14:33:39 +00:00
}
2022-07-18 10:51:49 +00:00
// 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 ) {
2022-08-10 13:22:32 +00:00
return nil , errdefs . NotImplemented ( errdefs . NotImplemented ( errors . New ( "not implemented" ) ) )
2022-07-05 14:33:39 +00:00
}
2022-07-18 10:51:49 +00:00
// LayerStoreStatus returns the status for each layer store
// called from info.go
func ( i * ImageService ) LayerStoreStatus ( ) [ ] [ 2 ] string {
2022-08-22 16:36:03 +00:00
// TODO(thaJeztah) do we want to add more details about the driver here?
return [ ] [ 2 ] string {
{ "driver-type" , string ( plugin . SnapshotPlugin ) } ,
}
2022-07-18 10:51:49 +00:00
}
2022-07-05 14:33:39 +00:00
// GetLayerMountID returns the mount ID for a layer
// called from daemon.go Daemon.Shutdown(), and Daemon.Cleanup() (cleanup is actually continerCleanup)
// TODO: needs to be refactored to Unmount (see callers), or removed and replaced with GetLayerByID
2022-07-18 10:51:49 +00:00
func ( i * ImageService ) GetLayerMountID ( cid string ) ( string , error ) {
2022-08-10 13:22:32 +00:00
return "" , errdefs . NotImplemented ( errors . New ( "not implemented" ) )
2022-07-05 14:33:39 +00:00
}
// Cleanup resources before the process is shutdown.
// called from daemon.go Daemon.Shutdown()
2022-07-18 08:57:11 +00:00
func ( i * ImageService ) Cleanup ( ) error {
2022-07-05 14:33:39 +00:00
return nil
}
2022-08-09 15:03:50 +00:00
// StorageDriver returns the name of the default storage-driver (snapshotter)
// used by the ImageService.
func ( i * ImageService ) StorageDriver ( ) string {
2022-08-03 09:20:54 +00:00
return i . snapshotter
2022-07-05 14:33:39 +00:00
}
2022-07-18 10:51:49 +00:00
// 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 {
2022-08-10 13:22:32 +00:00
return errdefs . NotImplemented ( errors . New ( "not implemented" ) )
2022-07-05 14:33:39 +00:00
}
// LayerDiskUsage returns the number of bytes used by layer stores
// called from disk_usage.go
2022-07-18 08:57:11 +00:00
func ( i * ImageService ) LayerDiskUsage ( ctx context . Context ) ( int64 , error ) {
2022-11-29 15:46:19 +00:00
var allLayersSize int64
2023-04-05 12:09:21 +00:00
// TODO(thaJeztah): do we need to take multiple snapshotters into account? See https://github.com/moby/moby/issues/45273
2022-11-29 15:46:19 +00:00
snapshotter := i . client . SnapshotService ( i . snapshotter )
snapshotter . Walk ( ctx , func ( ctx context . Context , info snapshots . Info ) error {
usage , err := snapshotter . Usage ( ctx , info . Name )
2022-09-01 08:30:34 +00:00
if err != nil {
2022-11-29 15:46:19 +00:00
return err
2022-09-01 08:30:34 +00:00
}
2022-11-29 15:46:19 +00:00
allLayersSize += usage . Size
return nil
2022-09-01 08:30:34 +00:00
} )
2022-11-29 15:46:19 +00:00
return allLayersSize , nil
2022-07-05 14:33:39 +00:00
}
2022-07-18 10:51:49 +00:00
// UpdateConfig values
//
// called from reload.go
func ( i * ImageService ) UpdateConfig ( maxDownloads , maxUploads int ) {
2022-07-05 14:33:39 +00:00
panic ( "not implemented" )
}
2022-07-18 10:51:49 +00:00
// GetLayerFolders returns the layer folders from an image RootFS.
func ( i * ImageService ) GetLayerFolders ( img * image . Image , rwLayer layer . RWLayer ) ( [ ] string , error ) {
2022-08-10 13:22:32 +00:00
return nil , errdefs . NotImplemented ( errors . New ( "not implemented" ) )
2022-07-05 14:33:39 +00:00
}
// GetContainerLayerSize returns the real size & virtual size of the container.
2023-03-06 15:02:37 +00:00
func ( i * ImageService ) GetContainerLayerSize ( ctx context . Context , containerID string ) ( int64 , int64 , error ) {
ctr := i . containers . Get ( containerID )
if ctr == nil {
return 0 , 0 , nil
}
2023-04-25 13:10:55 +00:00
snapshotter := i . client . SnapshotService ( ctr . Driver )
usage , err := snapshotter . Usage ( ctx , containerID )
if err != nil {
return 0 , 0 , err
}
imageManifest , err := getContainerImageManifest ( ctr )
if err != nil {
// Best efforts attempt to pick an image.
// We don't have platform information at this point, so we can only
// assume that the platform matches host.
// Otherwise this will give a wrong base image size (different
// platform), but should be close enough.
mfst , err := i . GetImageManifest ( ctx , ctr . Config . Image , imagetypes . GetImageOpts { } )
if err != nil {
// Log error, don't error out whole operation.
2023-06-23 00:33:17 +00:00
log . G ( ctx ) . WithFields ( logrus . Fields {
2023-04-25 13:10:55 +00:00
logrus . ErrorKey : err ,
"container" : containerID ,
} ) . Warn ( "empty ImageManifest, can't calculate base image size" )
return usage . Size , 0 , nil
}
imageManifest = * mfst
}
2023-03-06 15:02:37 +00:00
cs := i . client . ContentStore ( )
2023-04-25 13:10:55 +00:00
imageManifestBytes , err := content . ReadBlob ( ctx , cs , imageManifest )
2023-03-06 15:02:37 +00:00
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
}
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 )
2023-04-17 11:53:21 +00:00
snapShotSize , err := computeSnapshotSize ( chainIDs , snapshotSizeFn )
2023-03-06 15:02:37 +00:00
if err != nil {
return 0 , 0 , err
}
2023-04-17 11:53:21 +00:00
// TODO(thaJeztah): include content-store size for the image (similar to "GET /images/json")
return usage . Size , usage . Size + snapShotSize , nil
2022-07-05 14:33:39 +00:00
}
2023-04-25 13:10:55 +00:00
// getContainerImageManifest safely dereferences ImageManifest.
// ImageManifest can be nil for containers created with Docker Desktop with old
// containerd image store integration enabled which didn't set this field.
func getContainerImageManifest ( ctr * container . Container ) ( ocispec . Descriptor , error ) {
if ctr . ImageManifest == nil {
return ocispec . Descriptor { } , errdefs . InvalidParameter ( errors . New ( "container is missing ImageManifest (probably created on old version), please recreate it" ) )
}
return * ctr . ImageManifest , nil
}