mirror of
https://github.com/moby/moby.git
synced 2026-06-30 19:58:03 +00:00
c8d/pull: Keep the replaced image as dangling
With graphdrivers, the old image was still kept as a dangling image.
Keep the same behavior with containerd.
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit db40a6132b)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/leases"
|
||||
"github.com/containerd/containerd/pkg/snapshotters"
|
||||
"github.com/containerd/containerd/remotes/docker"
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
@@ -79,10 +80,43 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
|
||||
resolver, _ := i.newResolverFromAuthConfig(ctx, authConfig, ref)
|
||||
opts = append(opts, containerd.WithResolver(resolver))
|
||||
|
||||
old, err := i.resolveDescriptor(ctx, ref.String())
|
||||
oldImage, err := i.resolveImage(ctx, ref.String())
|
||||
if err != nil && !errdefs.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Will be set to the new image after pull succeeds.
|
||||
var outNewImg *containerd.Image
|
||||
|
||||
if oldImage.Target.Digest != "" {
|
||||
// Lease the old image content to prevent it from being garbage collected until we keep it as dangling image.
|
||||
lm := i.client.LeasesService()
|
||||
lease, err := lm.Create(ctx, leases.WithRandomID())
|
||||
if err != nil {
|
||||
return errdefs.System(fmt.Errorf("failed to create lease: %w", err))
|
||||
}
|
||||
|
||||
err = leaseContent(ctx, i.content, lm, lease, oldImage.Target)
|
||||
if err != nil {
|
||||
return errdefs.System(fmt.Errorf("failed to lease content: %w", err))
|
||||
}
|
||||
|
||||
// If the pulled image is different than the old image, we will keep the old image as a dangling image.
|
||||
defer func() {
|
||||
if outNewImg != nil {
|
||||
img := *outNewImg
|
||||
if img.Target().Digest != oldImage.Target.Digest {
|
||||
if err := i.ensureDanglingImage(ctx, oldImage); err != nil {
|
||||
log.G(ctx).WithError(err).Warn("failed to keep the previous image as dangling")
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := lm.Delete(ctx, lease); err != nil {
|
||||
log.G(ctx).WithError(err).Warn("failed to delete lease")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
p := platforms.Default()
|
||||
if platform != nil {
|
||||
p = platforms.Only(*platform)
|
||||
@@ -100,7 +134,6 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
|
||||
pp := pullProgress{store: i.content, showExists: true}
|
||||
finishProgress := jobs.showProgress(ctx, out, pp)
|
||||
|
||||
var outNewImg *containerd.Image
|
||||
defer func() {
|
||||
finishProgress()
|
||||
|
||||
@@ -116,7 +149,8 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
|
||||
if outNewImg != nil {
|
||||
img := *outNewImg
|
||||
progress.Message(out, "", "Digest: "+img.Target().Digest.String())
|
||||
writeStatus(out, reference.FamiliarString(ref), old.Digest != img.Target().Digest)
|
||||
newer := oldImage.Target.Digest != img.Target().Digest
|
||||
writeStatus(out, reference.FamiliarString(ref), newer)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/testutil/daemon"
|
||||
"github.com/docker/docker/testutil/registry"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go"
|
||||
@@ -195,3 +197,41 @@ func TestImagePullNonExisting(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImagePullKeepOldAsDangling(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "Can't run new daemons on Windows")
|
||||
|
||||
ctx := setupTest(t)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Cleanup(t)
|
||||
|
||||
apiClient := d.NewClientT(t)
|
||||
|
||||
inspect1, _, err := apiClient.ImageInspectWithRaw(ctx, "busybox:latest")
|
||||
assert.NilError(t, err)
|
||||
|
||||
prevID := inspect1.ID
|
||||
|
||||
t.Log(inspect1)
|
||||
|
||||
assert.NilError(t, apiClient.ImageTag(ctx, "busybox:latest", "alpine:latest"))
|
||||
|
||||
_, err = apiClient.ImageRemove(ctx, "busybox:latest", image.RemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
rc, err := apiClient.ImagePull(ctx, "alpine:latest", image.PullOptions{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
defer rc.Close()
|
||||
|
||||
var b bytes.Buffer
|
||||
_, _ = io.Copy(&b, rc)
|
||||
|
||||
t.Log(b.String())
|
||||
|
||||
_, _, err = apiClient.ImageInspectWithRaw(ctx, prevID)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user