@@ -10,6 +10,7 @@ import (
10
10
11
11
"github.com/containerd/containerd"
12
12
"github.com/containerd/containerd/images"
13
+ "github.com/containerd/containerd/leases"
13
14
"github.com/containerd/containerd/pkg/snapshotters"
14
15
"github.com/containerd/containerd/remotes/docker"
15
16
cerrdefs "github.com/containerd/errdefs"
@@ -79,10 +80,42 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
79
80
resolver , _ := i .newResolverFromAuthConfig (ctx , authConfig , ref )
80
81
opts = append (opts , containerd .WithResolver (resolver))
81
82
82
- old , err := i .resolveDescriptor (ctx , ref .String ())
83
+ oldImage , err := i .resolveImage (ctx , ref .String ())
83
84
if err != nil && ! errdefs .IsNotFound (err ) {
84
85
return err
85
86
}
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
+
86
119
p := platforms .Default ()
87
120
if platform != nil {
88
121
p = platforms .Only (* platform )
@@ -100,7 +133,6 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
100
133
pp := pullProgress {store : i .content , showExists : true }
101
134
finishProgress := jobs .showProgress (ctx , out , pp )
102
135
103
- var outNewImg * containerd.Image
104
136
defer func () {
105
137
finishProgress ()
106
138
@@ -114,9 +146,10 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
114
146
// Status: Downloaded newer image for hello-world:latest
115
147
// docker.io/library/hello-world:latest
116
148
if outNewImg != nil {
117
- img := * outNewImg
149
+ img := outNewImg
118
150
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 )
120
153
}
121
154
}()
122
155
@@ -202,7 +235,7 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
202
235
}
203
236
204
237
i .LogImageEvent (reference .FamiliarString (ref ), reference .FamiliarName (ref ), events .ActionPull )
205
- outNewImg = & img
238
+ outNewImg = img
206
239
return nil
207
240
}
208
241
0 commit comments