@@ -92,7 +92,7 @@ func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Ex
92
92
93
93
root := opt .Root
94
94
95
- if err := os .MkdirAll (root , 0711 ); err != nil {
95
+ if err := os .MkdirAll (root , 0o711 ); err != nil {
96
96
return nil , errors .Wrapf (err , "failed to create %s" , root )
97
97
}
98
98
@@ -205,7 +205,7 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root executor.Mount,
205
205
}
206
206
bundle := filepath .Join (w .root , id )
207
207
208
- if err := os .Mkdir (bundle , 0711 ); err != nil {
208
+ if err := os .Mkdir (bundle , 0o711 ); err != nil {
209
209
return err
210
210
}
211
211
defer os .RemoveAll (bundle )
@@ -216,7 +216,7 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root executor.Mount,
216
216
}
217
217
218
218
rootFSPath := filepath .Join (bundle , "rootfs" )
219
- if err := idtools .MkdirAllAndChown (rootFSPath , 0700 , identity ); err != nil {
219
+ if err := idtools .MkdirAllAndChown (rootFSPath , 0o700 , identity ); err != nil {
220
220
return err
221
221
}
222
222
if err := mount .All (rootMount , rootFSPath ); err != nil {
@@ -270,7 +270,7 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root executor.Mount,
270
270
return errors .Wrapf (err , "working dir %s points to invalid target" , newp )
271
271
}
272
272
if _ , err := os .Stat (newp ); err != nil {
273
- if err := idtools .MkdirAllAndChown (newp , 0755 , identity ); err != nil {
273
+ if err := idtools .MkdirAllAndChown (newp , 0o755 , identity ); err != nil {
274
274
return errors .Wrapf (err , "failed to create working directory %s" , newp )
275
275
}
276
276
}
@@ -287,50 +287,17 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root executor.Mount,
287
287
return err
288
288
}
289
289
290
- // runCtx/killCtx is used for extra check in case the kill command blocks
291
- runCtx , cancelRun := context .WithCancel (context .Background ())
292
- defer cancelRun ()
293
-
294
- ended := make (chan struct {})
295
- go func () {
296
- for {
297
- select {
298
- case <- ctx .Done ():
299
- killCtx , timeout := context .WithTimeout (context .Background (), 7 * time .Second )
300
- if err := w .runc .Kill (killCtx , id , int (syscall .SIGKILL ), nil ); err != nil {
301
- bklog .G (ctx ).Errorf ("failed to kill runc %s: %+v" , id , err )
302
- select {
303
- case <- killCtx .Done ():
304
- timeout ()
305
- cancelRun ()
306
- return
307
- default :
308
- }
309
- }
310
- timeout ()
311
- select {
312
- case <- time .After (50 * time .Millisecond ):
313
- case <- ended :
314
- return
315
- }
316
- case <- ended :
317
- return
318
- }
319
- }
320
- }()
321
-
322
290
bklog .G (ctx ).Debugf ("> creating %s %v" , id , meta .Args )
323
291
324
292
trace .SpanFromContext (ctx ).AddEvent ("Container created" )
325
- err = w .run (runCtx , id , bundle , process , func () {
293
+ err = w .run (ctx , id , bundle , process , func () {
326
294
startedOnce .Do (func () {
327
295
trace .SpanFromContext (ctx ).AddEvent ("Container started" )
328
296
if started != nil {
329
297
close (started )
330
298
}
331
299
})
332
300
})
333
- close (ended )
334
301
return exitError (ctx , err )
335
302
}
336
303
@@ -462,23 +429,87 @@ func (s *forwardIO) Stderr() io.ReadCloser {
462
429
return nil
463
430
}
464
431
465
- // startingProcess is to track the os process so we can send signals to it.
466
- type startingProcess struct {
467
- Process * os.Process
468
- ready chan struct {}
432
+ // procHandle is to track the os process so we can send signals to it.
433
+ type procHandle struct {
434
+ Process * os.Process
435
+ ready chan struct {}
436
+ ended chan struct {}
437
+ shutdown func ()
469
438
}
470
439
471
- // Release will free resources with a startingProcess.
472
- func (p * startingProcess ) Release () {
440
+ // runcProcessHandle will create a procHandle that will be monitored, where
441
+ // on ctx.Done the process will be killed. If the kill fails, then the cancel
442
+ // will be called. This is to allow for runc to go through its normal shutdown
443
+ // procedure if the ctx is canceled and to ensure there are no zombie processes
444
+ // left by runc.
445
+ func runcProcessHandle (ctx context.Context , id string ) (* procHandle , context.Context ) {
446
+ runcCtx , cancel := context .WithCancel (context .Background ())
447
+ p := & procHandle {
448
+ ready : make (chan struct {}),
449
+ ended : make (chan struct {}),
450
+ shutdown : cancel ,
451
+ }
452
+ // preserve the logger on the context used for the runc process handling
453
+ runcCtx = bklog .WithLogger (runcCtx , bklog .G (ctx ))
454
+
455
+ go func () {
456
+ // Wait for pid
457
+ select {
458
+ case <- ctx .Done ():
459
+ return // nothing to kill
460
+ case <- p .ready :
461
+ }
462
+
463
+ for {
464
+ select {
465
+ case <- ctx .Done ():
466
+ killCtx , timeout := context .WithTimeout (context .Background (), 7 * time .Second )
467
+ if err := p .Process .Kill (); err != nil {
468
+ bklog .G (ctx ).Errorf ("failed to kill runc %s: %+v" , id , err )
469
+ select {
470
+ case <- killCtx .Done ():
471
+ timeout ()
472
+ cancel ()
473
+ return
474
+ default :
475
+ }
476
+ }
477
+ timeout ()
478
+ select {
479
+ case <- time .After (50 * time .Millisecond ):
480
+ case <- p .ended :
481
+ return
482
+ }
483
+ case <- p .ended :
484
+ return
485
+ }
486
+ }
487
+ }()
488
+
489
+ return p , runcCtx
490
+ }
491
+
492
+ // Release will free resources with a procHandle.
493
+ func (p * procHandle ) Release () {
494
+ close (p .ended )
473
495
if p .Process != nil {
474
496
p .Process .Release ()
475
497
}
476
498
}
477
499
500
+ // Shutdown should be called after the runc process has exited. This will allow
501
+ // the signal handling and tty resize loops to exit, terminating the
502
+ // goroutines.
503
+ func (p * procHandle ) Shutdown () {
504
+ if p .shutdown != nil {
505
+ p .shutdown ()
506
+ }
507
+ }
508
+
478
509
// WaitForReady will wait until the Process has been populated or the
479
510
// provided context was cancelled. This should be called before using
480
511
// the Process field.
481
- func (p * startingProcess ) WaitForReady (ctx context.Context ) error {
512
+ func (p * procHandle ) WaitForReady (ctx context.Context ) error {
482
513
select {
483
514
case <- ctx .Done ():
484
515
return ctx .Err ()
@@ -490,7 +521,7 @@ func (p *startingProcess) WaitForReady(ctx context.Context) error {
490
521
// WaitForStart will record the pid reported by Runc via the channel.
491
522
// We wait for up to 10s for the runc process to start. If the started
492
523
// callback is non-nil it will be called after receiving the pid.
493
- func (p * startingProcess ) WaitForStart (ctx context.Context , startedCh <- chan int , started func ()) error {
524
+ func (p * procHandle ) WaitForStart (ctx context.Context , startedCh <- chan int , started func ()) error {
494
525
startedCtx , timeout := context .WithTimeout (ctx , 10 * time .Second )
495
526
defer timeout ()
496
527
var err error
@@ -515,7 +546,7 @@ func (p *startingProcess) WaitForStart(ctx context.Context, startedCh <-chan int
515
546
516
547
// handleSignals will wait until the runcProcess is ready then will
517
548
// send each signal received on the channel to the process.
518
- func handleSignals (ctx context.Context , runcProcess * startingProcess , signals <- chan syscall.Signal ) error {
549
+ func handleSignals (ctx context.Context , runcProcess * procHandle , signals <- chan syscall.Signal ) error {
519
550
if signals == nil {
520
551
return nil
521
552
}
0 commit comments