mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
8d0dc69027
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
278 lines
9.1 KiB
Go
278 lines
9.1 KiB
Go
package images // import "github.com/docker/docker/daemon/images"
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
|
|
"github.com/containerd/containerd/content"
|
|
"github.com/containerd/containerd/leases"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/docker/docker/container"
|
|
daemonevents "github.com/docker/docker/daemon/events"
|
|
"github.com/docker/docker/distribution/metadata"
|
|
"github.com/docker/docker/distribution/xfer"
|
|
"github.com/docker/docker/image"
|
|
"github.com/docker/docker/layer"
|
|
dockerreference "github.com/docker/docker/reference"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/libtrust"
|
|
"github.com/opencontainers/go-digest"
|
|
"github.com/pkg/errors"
|
|
"golang.org/x/sync/singleflight"
|
|
)
|
|
|
|
type containerStore interface {
|
|
// First is used by image delete
|
|
First(container.StoreFilter) *container.Container
|
|
// List is used by image prune, and image list
|
|
List() []*container.Container
|
|
// Get is used by CommitBuildStep
|
|
// TODO: remove, only used for CommitBuildStep
|
|
Get(string) *container.Container
|
|
}
|
|
|
|
// ImageServiceConfig is the configuration used to create a new ImageService
|
|
type ImageServiceConfig struct {
|
|
ContainerStore containerStore
|
|
DistributionMetadataStore metadata.Store
|
|
EventsService *daemonevents.Events
|
|
ImageStore image.Store
|
|
LayerStore layer.Store
|
|
MaxConcurrentDownloads int
|
|
MaxConcurrentUploads int
|
|
MaxDownloadAttempts int
|
|
ReferenceStore dockerreference.Store
|
|
RegistryService registry.Service
|
|
TrustKey libtrust.PrivateKey
|
|
ContentStore content.Store
|
|
Leases leases.Manager
|
|
ContentNamespace string
|
|
}
|
|
|
|
// NewImageService returns a new ImageService from a configuration
|
|
func NewImageService(config ImageServiceConfig) *ImageService {
|
|
return &ImageService{
|
|
containers: config.ContainerStore,
|
|
distributionMetadataStore: config.DistributionMetadataStore,
|
|
downloadManager: xfer.NewLayerDownloadManager(config.LayerStore, config.MaxConcurrentDownloads, xfer.WithMaxDownloadAttempts(config.MaxDownloadAttempts)),
|
|
eventsService: config.EventsService,
|
|
imageStore: &imageStoreWithLease{Store: config.ImageStore, leases: config.Leases, ns: config.ContentNamespace},
|
|
layerStore: config.LayerStore,
|
|
referenceStore: config.ReferenceStore,
|
|
registryService: config.RegistryService,
|
|
trustKey: config.TrustKey,
|
|
uploadManager: xfer.NewLayerUploadManager(config.MaxConcurrentUploads),
|
|
leases: config.Leases,
|
|
content: config.ContentStore,
|
|
contentNamespace: config.ContentNamespace,
|
|
}
|
|
}
|
|
|
|
// ImageService provides a backend for image management
|
|
type ImageService struct {
|
|
containers containerStore
|
|
distributionMetadataStore metadata.Store
|
|
downloadManager *xfer.LayerDownloadManager
|
|
eventsService *daemonevents.Events
|
|
imageStore image.Store
|
|
layerStore layer.Store
|
|
pruneRunning int32
|
|
referenceStore dockerreference.Store
|
|
registryService registry.Service
|
|
trustKey libtrust.PrivateKey
|
|
uploadManager *xfer.LayerUploadManager
|
|
leases leases.Manager
|
|
content content.Store
|
|
contentNamespace string
|
|
usage singleflight.Group
|
|
}
|
|
|
|
// DistributionServices provides daemon image storage services
|
|
type DistributionServices struct {
|
|
DownloadManager *xfer.LayerDownloadManager
|
|
V2MetadataService metadata.V2MetadataService
|
|
LayerStore layer.Store
|
|
ImageStore image.Store
|
|
ReferenceStore dockerreference.Store
|
|
}
|
|
|
|
// DistributionServices return services controlling daemon image storage
|
|
func (i *ImageService) DistributionServices() DistributionServices {
|
|
return DistributionServices{
|
|
DownloadManager: i.downloadManager,
|
|
V2MetadataService: metadata.NewV2MetadataService(i.distributionMetadataStore),
|
|
LayerStore: i.layerStore,
|
|
ImageStore: i.imageStore,
|
|
ReferenceStore: i.referenceStore,
|
|
}
|
|
}
|
|
|
|
// CountImages returns the number of images stored by ImageService
|
|
// called from info.go
|
|
func (i *ImageService) CountImages() int {
|
|
return i.imageStore.Len()
|
|
}
|
|
|
|
// Children returns the children image.IDs for a parent image.
|
|
// called from list.go to filter containers
|
|
// TODO: refactor to expose an ancestry for image.ID?
|
|
func (i *ImageService) Children(id image.ID) []image.ID {
|
|
return i.imageStore.Children(id)
|
|
}
|
|
|
|
// 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) {
|
|
var layerID layer.ChainID
|
|
if container.ImageID != "" {
|
|
img, err := i.imageStore.Get(container.ImageID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
layerID = img.RootFS.ChainID()
|
|
}
|
|
|
|
rwLayerOpts := &layer.CreateRWLayerOpts{
|
|
MountLabel: container.MountLabel,
|
|
InitFunc: initFunc,
|
|
StorageOpt: container.HostConfig.StorageOpt,
|
|
}
|
|
|
|
return i.layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts)
|
|
}
|
|
|
|
// GetLayerByID returns a layer by ID
|
|
// called from daemon.go Daemon.restore(), and Daemon.containerExport().
|
|
func (i *ImageService) GetLayerByID(cid string) (layer.RWLayer, error) {
|
|
return i.layerStore.GetRWLayer(cid)
|
|
}
|
|
|
|
// LayerStoreStatus returns the status for each layer store
|
|
// called from info.go
|
|
func (i *ImageService) LayerStoreStatus() [][2]string {
|
|
return i.layerStore.DriverStatus()
|
|
}
|
|
|
|
// 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
|
|
func (i *ImageService) GetLayerMountID(cid string) (string, error) {
|
|
return i.layerStore.GetMountID(cid)
|
|
}
|
|
|
|
// Cleanup resources before the process is shutdown.
|
|
// called from daemon.go Daemon.Shutdown()
|
|
func (i *ImageService) Cleanup() error {
|
|
if err := i.layerStore.Cleanup(); err != nil {
|
|
return errors.Wrap(err, "error during layerStore.Cleanup()")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// StorageDriver returns the name of the storage driver used by the ImageService.
|
|
func (i *ImageService) StorageDriver() string {
|
|
return i.layerStore.DriverName()
|
|
}
|
|
|
|
// 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 {
|
|
metaData, err := i.layerStore.ReleaseRWLayer(rwlayer)
|
|
layer.LogReleaseMetadata(metaData)
|
|
if err != nil && !errors.Is(err, layer.ErrMountDoesNotExist) && !errors.Is(err, os.ErrNotExist) {
|
|
return errors.Wrapf(err, "driver %q failed to remove root filesystem",
|
|
i.layerStore.DriverName())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LayerDiskUsage returns the number of bytes used by layer stores
|
|
// called from disk_usage.go
|
|
func (i *ImageService) LayerDiskUsage(ctx context.Context) (int64, error) {
|
|
ch := i.usage.DoChan("LayerDiskUsage", func() (interface{}, error) {
|
|
var allLayersSize int64
|
|
layerRefs := i.getLayerRefs()
|
|
allLayers := i.layerStore.Map()
|
|
for _, l := range allLayers {
|
|
select {
|
|
case <-ctx.Done():
|
|
return allLayersSize, ctx.Err()
|
|
default:
|
|
size := l.DiffSize()
|
|
if _, ok := layerRefs[l.ChainID()]; ok {
|
|
allLayersSize += size
|
|
}
|
|
}
|
|
}
|
|
return allLayersSize, nil
|
|
})
|
|
select {
|
|
case <-ctx.Done():
|
|
return 0, ctx.Err()
|
|
case res := <-ch:
|
|
if res.Err != nil {
|
|
return 0, res.Err
|
|
}
|
|
return res.Val.(int64), nil
|
|
}
|
|
}
|
|
|
|
func (i *ImageService) getLayerRefs() map[layer.ChainID]int {
|
|
tmpImages := i.imageStore.Map()
|
|
layerRefs := map[layer.ChainID]int{}
|
|
for id, img := range tmpImages {
|
|
dgst := digest.Digest(id)
|
|
if len(i.referenceStore.References(dgst)) == 0 && len(i.imageStore.Children(id)) != 0 {
|
|
continue
|
|
}
|
|
|
|
rootFS := *img.RootFS
|
|
rootFS.DiffIDs = nil
|
|
for _, id := range img.RootFS.DiffIDs {
|
|
rootFS.Append(id)
|
|
chid := rootFS.ChainID()
|
|
layerRefs[chid]++
|
|
}
|
|
}
|
|
|
|
return layerRefs
|
|
}
|
|
|
|
// ImageDiskUsage returns information about image data disk usage.
|
|
func (i *ImageService) ImageDiskUsage(ctx context.Context) ([]*types.ImageSummary, error) {
|
|
ch := i.usage.DoChan("ImageDiskUsage", func() (interface{}, error) {
|
|
// Get all top images with extra attributes
|
|
imgs, err := i.Images(ctx, types.ImageListOptions{
|
|
Filters: filters.NewArgs(),
|
|
SharedSize: true,
|
|
ContainerCount: true,
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to retrieve image list")
|
|
}
|
|
return imgs, nil
|
|
})
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
case res := <-ch:
|
|
if res.Err != nil {
|
|
return nil, res.Err
|
|
}
|
|
return res.Val.([]*types.ImageSummary), nil
|
|
}
|
|
}
|
|
|
|
// UpdateConfig values
|
|
//
|
|
// called from reload.go
|
|
func (i *ImageService) UpdateConfig(maxDownloads, maxUploads int) {
|
|
if i.downloadManager != nil && maxDownloads != 0 {
|
|
i.downloadManager.SetConcurrency(maxDownloads)
|
|
}
|
|
if i.uploadManager != nil && maxUploads != 0 {
|
|
i.uploadManager.SetConcurrency(maxUploads)
|
|
}
|
|
}
|