From 06619763a25b3f651c198baaebbd5f01a2315976 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 5 Aug 2022 09:57:08 +0200 Subject: [PATCH] remove GetLayerByID from ImageService interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolas De Loof Co-authored-by: Paweł Gronowski Signed-off-by: Sebastiaan van Stijn --- api/server/router/container/backend.go | 2 +- .../router/container/container_routes.go | 2 +- daemon/containerd/image_exporter.go | 11 ++++ daemon/containerd/service.go | 6 --- daemon/daemon.go | 17 +++++-- daemon/export.go | 50 ++++++------------- daemon/image_service.go | 2 +- daemon/images/image_exporter.go | 24 +++++++++ daemon/images/service.go | 4 +- 9 files changed, 68 insertions(+), 50 deletions(-) diff --git a/api/server/router/container/backend.go b/api/server/router/container/backend.go index eea44d8b7a..0c53282b38 100644 --- a/api/server/router/container/backend.go +++ b/api/server/router/container/backend.go @@ -25,7 +25,7 @@ type execBackend interface { type copyBackend interface { ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) ContainerCopy(name string, res string) (io.ReadCloser, error) - ContainerExport(name string, out io.Writer) error + ContainerExport(ctx context.Context, name string, out io.Writer) error ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error) } diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go index 9436ab46d8..5f4b63a1d2 100644 --- a/api/server/router/container/container_routes.go +++ b/api/server/router/container/container_routes.go @@ -174,7 +174,7 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response } func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - return s.backend.ContainerExport(vars["name"], w) + return s.backend.ContainerExport(ctx, vars["name"], w) } type bodyOnStartError struct{} diff --git a/daemon/containerd/image_exporter.go b/daemon/containerd/image_exporter.go index e15abe509d..44fada7c46 100644 --- a/daemon/containerd/image_exporter.go +++ b/daemon/containerd/image_exporter.go @@ -9,8 +9,10 @@ import ( cerrdefs "github.com/containerd/containerd/errdefs" containerdimages "github.com/containerd/containerd/images" "github.com/containerd/containerd/images/archive" + "github.com/containerd/containerd/mount" cplatforms "github.com/containerd/containerd/platforms" "github.com/docker/distribution/reference" + "github.com/docker/docker/container" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/platforms" "github.com/docker/docker/pkg/streamformatter" @@ -19,6 +21,15 @@ import ( "github.com/sirupsen/logrus" ) +func (i *ImageService) PerformWithBaseFS(ctx context.Context, c *container.Container, fn func(root string) error) error { + snapshotter := i.client.SnapshotService(i.snapshotter) + mounts, err := snapshotter.Mounts(ctx, c.ID) + if err != nil { + return err + } + return mount.WithTempMount(ctx, mounts, fn) +} + // ExportImage exports a list of images to the given output stream. The // exported images are archived into a tar when written to the output // stream. All images with the given tag and all versions containing diff --git a/daemon/containerd/service.go b/daemon/containerd/service.go index 7a768e681b..f003c4e2b0 100644 --- a/daemon/containerd/service.go +++ b/daemon/containerd/service.go @@ -75,12 +75,6 @@ func (i *ImageService) CreateLayer(container *container.Container, initFunc laye return nil, errdefs.NotImplemented(errdefs.NotImplemented(errors.New("not implemented"))) } -// 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 nil, errdefs.NotImplemented(errors.New("not implemented")) -} - // LayerStoreStatus returns the status for each layer store // called from info.go func (i *ImageService) LayerStoreStatus() [][2]string { diff --git a/daemon/daemon.go b/daemon/daemon.go index ec5142faa9..6d3a3aa355 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -218,6 +218,11 @@ func (daemon *Daemon) RegistryHosts() docker.RegistryHosts { return resolver.NewRegistryConfig(m) } +// layerAccessor may be implemented by ImageService +type layerAccessor interface { + GetLayerByID(cid string) (layer.RWLayer, error) +} + func (daemon *Daemon) restore() error { var mapLock sync.Mutex containers := make(map[string]*container.Container) @@ -259,12 +264,14 @@ func (daemon *Daemon) restore() error { log.Debugf("not restoring container because it was created with another storage driver (%s)", c.Driver) return } - rwlayer, err := daemon.imageService.GetLayerByID(c.ID) - if err != nil { - log.WithError(err).Error("failed to load container mount") - return + if accessor, ok := daemon.imageService.(layerAccessor); ok { + rwlayer, err := accessor.GetLayerByID(c.ID) + if err != nil { + log.WithError(err).Error("failed to load container mount") + return + } + c.RWLayer = rwlayer } - c.RWLayer = rwlayer log.WithFields(logrus.Fields{ "running": c.IsRunning(), "paused": c.IsPaused(), diff --git a/daemon/export.go b/daemon/export.go index 8a14430840..6441c5f092 100644 --- a/daemon/export.go +++ b/daemon/export.go @@ -1,6 +1,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( + "context" "fmt" "io" @@ -8,12 +9,11 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/chrootarchive" - "github.com/docker/docker/pkg/ioutils" ) // ContainerExport writes the contents of the container to the given // writer. An error is returned if the container cannot be found. -func (daemon *Daemon) ContainerExport(name string, out io.Writer) error { +func (daemon *Daemon) ContainerExport(ctx context.Context, name string, out io.Writer) error { ctr, err := daemon.GetContainer(name) if err != nil { return err @@ -33,49 +33,31 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error { return errdefs.Conflict(err) } - data, err := daemon.containerExport(ctr) + err = daemon.containerExport(ctx, ctr, out) if err != nil { return fmt.Errorf("Error exporting container %s: %v", name, err) } - defer data.Close() - // Stream the entire contents of the container (basically a volatile snapshot) - if _, err := io.Copy(out, data); err != nil { - return fmt.Errorf("Error exporting container %s: %v", name, err) - } return nil } -func (daemon *Daemon) containerExport(container *container.Container) (arch io.ReadCloser, err error) { - rwlayer, err := daemon.imageService.GetLayerByID(container.ID) - if err != nil { - return nil, err - } - defer func() { +func (daemon *Daemon) containerExport(ctx context.Context, container *container.Container, out io.Writer) error { + err := daemon.imageService.PerformWithBaseFS(ctx, container, func(basefs string) error { + archv, err := chrootarchive.Tar(basefs, &archive.TarOptions{ + Compression: archive.Uncompressed, + IDMap: daemon.idMapping, + }, basefs) if err != nil { - daemon.imageService.ReleaseLayer(rwlayer) + return err } - }() - basefs, err := rwlayer.Mount(container.GetMountLabel()) - if err != nil { - return nil, err - } - - archv, err := chrootarchive.Tar(basefs, &archive.TarOptions{ - Compression: archive.Uncompressed, - IDMap: daemon.idMapping, - }, basefs) - if err != nil { - rwlayer.Unmount() - return nil, err - } - arch = ioutils.NewReadCloserWrapper(archv, func() error { - err := archv.Close() - rwlayer.Unmount() - daemon.imageService.ReleaseLayer(rwlayer) + // Stream the entire contents of the container (basically a volatile snapshot) + _, err = io.Copy(out, archv) return err }) + if err != nil { + return err + } daemon.LogContainerEvent(container, "export") - return arch, err + return nil } diff --git a/daemon/image_service.go b/daemon/image_service.go index 005c17736b..25b8af82a9 100644 --- a/daemon/image_service.go +++ b/daemon/image_service.go @@ -30,6 +30,7 @@ type ImageService interface { CreateImage(config []byte, parent string) (builder.Image, error) ImageDelete(ctx context.Context, imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error) ExportImage(ctx context.Context, names []string, outStream io.Writer) error + PerformWithBaseFS(ctx context.Context, c *container.Container, fn func(string) error) error LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error Images(ctx context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error) LogImageEvent(imageID, refName, action string) @@ -52,7 +53,6 @@ type ImageService interface { GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) CreateLayer(container *container.Container, initFunc layer.MountInit) (layer.RWLayer, error) - GetLayerByID(cid string) (layer.RWLayer, error) LayerStoreStatus() [][2]string GetLayerMountID(cid string) (string, error) ReleaseLayer(rwlayer layer.RWLayer) error diff --git a/daemon/images/image_exporter.go b/daemon/images/image_exporter.go index 2ab4af1a83..88877b01c6 100644 --- a/daemon/images/image_exporter.go +++ b/daemon/images/image_exporter.go @@ -4,7 +4,9 @@ import ( "context" "io" + "github.com/docker/docker/container" "github.com/docker/docker/image/tarexport" + "github.com/sirupsen/logrus" ) // ExportImage exports a list of images to the given output stream. The @@ -17,6 +19,28 @@ func (i *ImageService) ExportImage(ctx context.Context, names []string, outStrea return imageExporter.Save(names, outStream) } +func (i *ImageService) PerformWithBaseFS(ctx context.Context, c *container.Container, fn func(root string) error) error { + rwlayer, err := i.layerStore.GetRWLayer(c.ID) + if err != nil { + return err + } + defer func() { + if err != nil { + err2 := i.ReleaseLayer(rwlayer) + if err2 != nil { + logrus.WithError(err2).WithField("container", c.ID).Warn("Failed to release layer") + } + } + }() + + basefs, err := rwlayer.Mount(c.GetMountLabel()) + if err != nil { + return err + } + + return fn(basefs) +} + // LoadImage uploads a set of images into the repository. This is the // complement of ExportImage. The input stream is an uncompressed tar // ball containing images and metadata. diff --git a/daemon/images/service.go b/daemon/images/service.go index b6f431debf..ab51c396f5 100644 --- a/daemon/images/service.go +++ b/daemon/images/service.go @@ -136,7 +136,7 @@ func (i *ImageService) CreateLayer(container *container.Container, initFunc laye } // GetLayerByID returns a layer by ID -// called from daemon.go Daemon.restore(), and Daemon.containerExport(). +// called from daemon.go Daemon.restore(). func (i *ImageService) GetLayerByID(cid string) (layer.RWLayer, error) { return i.layerStore.GetRWLayer(cid) } @@ -169,7 +169,7 @@ func (i *ImageService) StorageDriver() string { } // ReleaseLayer releases a layer allowing it to be removed -// called from delete.go Daemon.cleanupContainer(), and Daemon.containerExport() +// called from delete.go Daemon.cleanupContainer(). func (i *ImageService) ReleaseLayer(rwlayer layer.RWLayer) error { metaData, err := i.layerStore.ReleaseRWLayer(rwlayer) layer.LogReleaseMetadata(metaData)