@@ -2,7 +2,9 @@ package container
2
2
3
3
import (
4
4
"context"
5
+ "encoding/json"
5
6
"errors"
7
+ "fmt"
6
8
"io"
7
9
"net"
8
10
"syscall"
@@ -16,7 +18,9 @@ import (
16
18
"github.com/docker/cli/internal/test/notary"
17
19
"github.com/docker/docker/api/types"
18
20
"github.com/docker/docker/api/types/container"
21
+ "github.com/docker/docker/api/types/image"
19
22
"github.com/docker/docker/api/types/network"
23
+ "github.com/docker/docker/pkg/jsonmessage"
20
24
specs "github.com/opencontainers/image-spec/specs-go/v1"
21
25
"github.com/spf13/pflag"
22
26
"gotest.tools/v3/assert"
@@ -189,6 +193,87 @@ func TestRunAttachTermination(t *testing.T) {
189
193
}
190
194
}
191
195
196
+ func TestRunPullTermination (t * testing.T ) {
197
+ ctx , cancel := context .WithCancel (context .Background ())
198
+ t .Cleanup (cancel )
199
+
200
+ attachCh := make (chan struct {})
201
+ fakeCLI := test .NewFakeCli (& fakeClient {
202
+ createContainerFunc : func (config * container.Config , hostConfig * container.HostConfig , networkingConfig * network.NetworkingConfig ,
203
+ platform * specs.Platform , containerName string ,
204
+ ) (container.CreateResponse , error ) {
205
+ select {
206
+ case <- ctx .Done ():
207
+ return container.CreateResponse {}, ctx .Err ()
208
+ default :
209
+ }
210
+ return container.CreateResponse {}, fakeNotFound {}
211
+ },
212
+ containerAttachFunc : func (ctx context.Context , containerID string , options container.AttachOptions ) (types.HijackedResponse , error ) {
213
+ return types.HijackedResponse {}, errors .New ("shouldn't try to attach to a container" )
214
+ },
215
+ imageCreateFunc : func (ctx context.Context , parentReference string , options image.CreateOptions ) (io.ReadCloser , error ) {
216
+ server , client := net .Pipe ()
217
+ t .Cleanup (func () {
218
+ _ = server .Close ()
219
+ })
220
+ go func () {
221
+ enc := json .NewEncoder (server )
222
+ for i := 0 ; i < 100 ; i ++ {
223
+ select {
224
+ case <- ctx .Done ():
225
+ assert .NilError (t , server .Close (), "failed to close imageCreateFunc server" )
226
+ return
227
+ default :
228
+ }
229
+ assert .NilError (t , enc .Encode (jsonmessage.JSONMessage {
230
+ Status : "Downloading" ,
231
+ ID : fmt .Sprintf ("id-%d" , i ),
232
+ TimeNano : time .Now ().UnixNano (),
233
+ Time : time .Now ().Unix (),
234
+ Progress : & jsonmessage.JSONProgress {
235
+ Current : int64 (i ),
236
+ Total : 100 ,
237
+ Start : 0 ,
238
+ },
239
+ }))
240
+ time .Sleep (100 * time .Millisecond )
241
+ }
242
+ }()
243
+ attachCh <- struct {}{}
244
+ return client , nil
245
+ },
246
+ Version : "1.30" ,
247
+ })
248
+
249
+ cmd := NewRunCommand (fakeCLI )
250
+ cmd .SetOut (io .Discard )
251
+ cmd .SetErr (io .Discard )
252
+ cmd .SetArgs ([]string {"foobar:latest" })
253
+
254
+ cmdErrC := make (chan error , 1 )
255
+ go func () {
256
+ cmdErrC <- cmd .ExecuteContext (ctx )
257
+ }()
258
+
259
+ select {
260
+ case <- time .After (5 * time .Second ):
261
+ t .Fatal ("imageCreateFunc was not called before the timeout" )
262
+ case <- attachCh :
263
+ }
264
+
265
+ cancel ()
266
+
267
+ select {
268
+ case cmdErr := <- cmdErrC :
269
+ assert .Equal (t , cmdErr , cli.StatusError {
270
+ StatusCode : 125 ,
271
+ })
272
+ case <- time .After (10 * time .Second ):
273
+ t .Fatal ("cmd did not return before the timeout" )
274
+ }
275
+ }
276
+
192
277
func TestRunCommandWithContentTrustErrors (t * testing.T ) {
193
278
testCases := []struct {
194
279
name string
0 commit comments