@@ -2,7 +2,9 @@ package container
22
33import (
44"context"
5+ "encoding/json"
56"errors"
7+ "fmt"
68"io"
79"net"
810"syscall"
@@ -16,7 +18,9 @@ import (
1618"github.com/docker/cli/internal/test/notary"
1719"github.com/docker/docker/api/types"
1820"github.com/docker/docker/api/types/container"
21+ "github.com/docker/docker/api/types/image"
1922"github.com/docker/docker/api/types/network"
23+ "github.com/docker/docker/pkg/jsonmessage"
2024specs "github.com/opencontainers/image-spec/specs-go/v1"
2125"github.com/spf13/pflag"
2226"gotest.tools/v3/assert"
@@ -217,6 +221,86 @@ func TestRunAttachTermination(t *testing.T) {
217221}
218222}
219223
224+ func TestRunPullTermination (t * testing.T ) {
225+ ctx , cancel := context .WithCancel (context .Background ())
226+ t .Cleanup (cancel )
227+
228+ attachCh := make (chan struct {})
229+ fakeCLI := test .NewFakeCli (& fakeClient {
230+ createContainerFunc : func (config * container.Config , hostConfig * container.HostConfig , networkingConfig * network.NetworkingConfig , platform * specs.Platform , containerName string ) (container.CreateResponse , error ) {
231+ select {
232+ case <- ctx .Done ():
233+ return container.CreateResponse {}, ctx .Err ()
234+ default :
235+ }
236+ return container.CreateResponse {}, fakeNotFound {}
237+ },
238+ containerAttachFunc : func (ctx context.Context , containerID string , options container.AttachOptions ) (types.HijackedResponse , error ) {
239+ return types.HijackedResponse {}, errors .New ("shouldn't try to attach to a container" )
240+ },
241+ imageCreateFunc : func (ctx context.Context , parentReference string , options image.CreateOptions ) (io.ReadCloser , error ) {
242+ server , client := net .Pipe ()
243+ t .Cleanup (func () {
244+ _ = server .Close ()
245+ })
246+ go func () {
247+ enc := json .NewEncoder (server )
248+ for i := 0 ; i < 100 ; i ++ {
249+ select {
250+ case <- ctx .Done ():
251+ assert .NilError (t , server .Close (), "failed to close imageCreateFunc server" )
252+ return
253+ default :
254+ }
255+ assert .NilError (t , enc .Encode (jsonmessage.JSONMessage {
256+ Status : "Downloading" ,
257+ ID : fmt .Sprintf ("id-%d" , i ),
258+ TimeNano : time .Now ().UnixNano (),
259+ Time : time .Now ().Unix (),
260+ Progress : & jsonmessage.JSONProgress {
261+ Current : int64 (i ),
262+ Total : 100 ,
263+ Start : 0 ,
264+ },
265+ }))
266+ time .Sleep (100 * time .Millisecond )
267+ }
268+ }()
269+ attachCh <- struct {}{}
270+ return client , nil
271+ },
272+ Version : "1.30" ,
273+ })
274+
275+ cmd := NewRunCommand (fakeCLI )
276+ cmd .SetOut (io .Discard )
277+ cmd .SetErr (io .Discard )
278+ cmd .SetArgs ([]string {"foobar:latest" })
279+
280+ cmdErrC := make (chan error , 1 )
281+ go func () {
282+ cmdErrC <- cmd .ExecuteContext (ctx )
283+ }()
284+
285+ select {
286+ case <- time .After (5 * time .Second ):
287+ t .Fatal ("imageCreateFunc was not called before the timeout" )
288+ case <- attachCh :
289+ }
290+
291+ cancel ()
292+
293+ select {
294+ case cmdErr := <- cmdErrC :
295+ assert .Equal (t , cmdErr , cli.StatusError {
296+ StatusCode : 125 ,
297+ Status : "docker: context canceled\n \n Run 'docker run --help' for more information" ,
298+ })
299+ case <- time .After (10 * time .Second ):
300+ t .Fatal ("cmd did not return before the timeout" )
301+ }
302+ }
303+
220304func TestRunCommandWithContentTrustErrors (t * testing.T ) {
221305testCases := []struct {
222306name string
0 commit comments