8000 Merge pull request #48380 from vvoland/48374-27.x · moby/moby@cd7746d · GitHub
[go: up one dir, main page]

Skip to content

Commit cd7746d

Browse files
authored
Merge pull request #48380 from vvoland/48374-27.x
[27.x backport] c8d/pull: Keep the replaced image as dangling
2 parents 2a13a38 + e854a5c commit cd7746d

File tree

2 files changed

+78
-5
lines changed

2 files changed

+78
-5
lines changed

daemon/containerd/image_pull.go

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/containerd/containerd"
1212
"github.com/containerd/containerd/images"
13+
"github.com/containerd/containerd/leases"
1314
"github.com/containerd/containerd/pkg/snapshotters"
1415
"github.com/containerd/containerd/remotes/docker"
1516
cerrdefs "github.com/containerd/errdefs"
@@ -79,10 +80,42 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
7980
resolver, _ := i.newResolverFromAuthConfig(ctx, authConfig, ref)
8081
opts = append(opts, containerd.WithResolver(resolver))
8182

82-
old, err := i.resolveDescriptor(ctx, ref.String())
83+
oldImage, err := i.resolveImage(ctx, ref.String())
8384
if err != nil && !errdefs.IsNotFound(err) {
8485
return err
8586
}
87+
88+
// Will be set to the new image after pull succeeds.
89+
var outNewImg containerd.Image
90+
91+
if oldImage.Target.Digest != "" {
92+
// Lease the old image content to prevent it from being garbage collected until we keep it as dangling image.
93+
lm := i.client.LeasesService()
94+
lease, err := lm.Create(ctx, leases.WithRandomID())
95+
if err != nil {
96+
return errdefs.System(fmt.Errorf("failed to create lease: %w", err))
97+
}
98+
99+
err = leaseContent(ctx, i.content, lm, lease, oldImage.Target)
100+
if err != nil {
101+
return errdefs.System(fmt.Errorf("failed to lease content: %w", err))
102+
}
103+
104+
// If the pulled image is different than the old image, we will keep the old image as a dangling image.
105+
defer func() {
106+
if outNewImg != nil {
107+
if outNewImg.Target().Digest != oldImage.Target.Digest {
108+
if err := i.ensureDanglingImage(ctx, oldImage); err != nil {
109+
log.G(ctx).WithError(err).Warn("failed to keep the previous image as dangling")
110+
}
111+
}
112+
}
113+
if err := lm.Delete(ctx, lease); err != nil {
114+
log.G(ctx).WithError(err).Warn("failed to delete lease")
115+
}
116+
}()
117+
}
118+
86119
p := platforms.Default()
87120
if platform != nil {
88121
p = platforms.Only(*platform)
@@ -100,7 +133,6 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
100133
pp := pullProgress{store: i.content, showExists: true}
101134
finishProgress := jobs.showProgress(ctx, out, pp)
102135

103-
var outNewImg *containerd.Image
104136
defer func() {
105137
finishProgress()
106138

@@ -114,9 +146,10 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
114146
// Status: Downloaded newer image for hello-world:latest
115147
// docker.io/library/hello-world:latest
116148
if outNewImg != nil {
117-
img := *outNewImg
149+
img := outNewImg
118150
progress.Message(out, "", "Digest: "+img.Target().Digest.String())
119-
writeStatus(out, reference.FamiliarString(ref), old.Digest != img.Target().Digest)
151+
newer := oldImage.Target.Digest != img.Target().Digest
152+
writeStatus(out, reference.FamiliarString(ref), newer)
120153
}
121154
}()
122155

@@ -202,7 +235,7 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
202235
}
203236

204237
i.LogImageEvent(reference.FamiliarString(ref), reference.FamiliarName(ref), events.ActionPull)
205-
outNewImg = &img
238+
outNewImg = img
206239
return nil
207240
}
208241

integration/image/pull_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package image
22

33
import (
4+
"bytes"
45
"context"
56
"encoding/json"
67
"fmt"
@@ -17,6 +18,7 @@ import (
1718
"github.com/containerd/platforms"
1819
"github.com/docker/docker/api/types/image"
1920
"github.com/docker/docker/errdefs"
21+
"github.com/docker/docker/testutil/daemon"
2022
"github.com/docker/docker/testutil/registry"
2123
"github.com/opencontainers/go-digest"
2224
"github.com/opencontainers/image-spec/specs-go"
@@ -195,3 +197,41 @@ func TestImagePullNonExisting(t *testing.T) {
195197
})
196198
}
197199
}
200+
201+
func TestImagePullKeepOldAsDangling(t *testing.T) {
202+
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
203+
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "Can't run new daemons on Windows")
204+
205+
ctx := setupTest(t)
206+
207+
d := daemon.New(t)
208+
d.StartWithBusybox(ctx, t)
209+
defer d.Cleanup(t)
< 22C7 /code>
210+
211+
apiClient := d.NewClientT(t)
212+
213+
inspect1, _, err := apiClient.ImageInspectWithRaw(ctx, "busybox:latest")
214+
assert.NilError(t, err)
215+
216+
prevID := inspect1.ID
217+
218+
t.Log(inspect1)
219+
220+
assert.NilError(t, apiClient.ImageTag(ctx, "busybox:latest", "alpine:latest"))
221+
222+
_, err = apiClient.ImageRemove(ctx, "busybox:latest", image.RemoveOptions{})
223+
assert.NilError(t, err)
224+
225+
rc, err := apiClient.ImagePull(ctx, "alpine:latest", image.PullOptions{})
226+
assert.NilError(t, err)
227+
228+
defer rc.Close()
229+
230+
var b bytes.Buffer
231+
_, _ = io.Copy(&b, rc)
232+
233+
t.Log(b.String())
234+
235+
_, _, err = apiClient.ImageInspectWithRaw(ctx, prevID)
236+
assert.NilError(t, err)
237+
}

0 commit comments

Comments
 (0)
0