@@ -71,6 +71,8 @@ type local struct {
71
71
72
72
processesMu sync.Mutex
73
73
processes map [string ]int32
74
+
75
+ fcControlSocket * net.UnixListener
74
76
}
75
77
76
78
func newLocal (ic * plugin.InitContext ) (* local , error ) {
@@ -243,7 +245,7 @@ func (s *local) StopVM(requestCtx context.Context, req *proto.StopVMRequest) (*e
243
245
defer client .Close ()
244
246
245
247
resp , shimErr := client .StopVM (requestCtx , req )
246
- waitErr := s .waitForShimToExit (requestCtx , req .VMID )
248
+ waitErr := s .waitForShimToExit (requestCtx , req .VMID , false )
247
249
248
250
// Assuming the shim is returning containerd's error code, return the error as is if possible.
249
251
if waitErr == nil {
@@ -252,7 +254,7 @@ func (s *local) StopVM(requestCtx context.Context, req *proto.StopVMRequest) (*e
252
254
return resp , multierror .Append (shimErr , waitErr ).ErrorOrNil ()
253
255
}
254
256
255
- func (s * local ) waitForShimToExit (ctx context.Context , vmID string ) error {
257
+ func (s * local ) waitForShimToExit (ctx context.Context , vmID string , killShim bool ) error {
256
258
socketAddr , err := fcShim .SocketAddress (ctx , vmID )
257
259
if err != nil {
258
260
return err
@@ -267,6 +269,17 @@ func (s *local) waitForShimToExit(ctx context.Context, vmID string) error {
267
269
}
268
270
defer delete (s .processes , socketAddr )
269
271
272
+ if killShim {
273
+ s .logger .Debug ("Killing shim" )
274
+
275
+ if err := syscall .Kill (int (pid ), 9 ); err != nil {
276
+ s .logger .WithError (err ).Error ("Failed to kill shim process" )
277
+ return err
278
+ }
279
+
280
+ return nil
281
+ }
282
+
270
283
return internal .WaitForPidToExit (ctx , stopVMInterval , pid )
271
284
}
272
285
@@ -464,6 +477,150 @@ func setShimOOMScore(shimPid int) error {
464
477
return nil
465
478
}
466
479
480
+ func (s * local ) loadShim (ctx context.Context , ns , vmID , containerdAddress string ) (* exec.Cmd , error ) {
481
+ logger := s .logger .WithField ("vmID" , vmID )
482
+ logger .Debug ("Loading shim" )
483
+
484
+ shimSocketAddress , err := fcShim .SocketAddress (ctx , vmID )
485
+ if err != nil {
486
+ err = errors .Wrap (err , "failed to obtain shim socket address" )
487
+ s .logger .WithError (err ).Error ()
488
+ return nil , err
489
+ }
490
+
491
+ shimSocket , err := shim .NewSocket (shimSocketAddress )
492
+ if isEADDRINUSE (err ) {
493
+ return nil , status .Errorf (codes .AlreadyExists , "VM with ID %q already exists (socket: %q)" , vmID , shimSocketAddress )
494
+ } else if err != nil {
495
+ err = errors .Wrapf (err , "failed to open shim socket at address %q" , shimSocketAddress )
496
+ s .logger .WithError (err ).Error ()
497
+ return nil , err
498
+ }
499
+
500
+ // If we're here, there is no pre-existing shim for this VMID, so we spawn a new one
501
+ defer shimSocket .Close ()
502
+ if err := os .Mkdir (s .config .ShimBaseDir , 0700 ); err != nil && ! os .IsExist (err ) {
503
+ s .logger .WithError (err ).Error ()
504
+ return nil , errors .Wrapf (err , "failed to make shim base directory: %s" , s .config .ShimBaseDir )
505
+ }
506
+
507
+ shimDir , err := vm .ShimDir (s .config .ShimBaseDir , ns , vmID )
508
+ if err != nil {
509
+ err = errors .Wrapf (err , "failed to build shim path" )
510
+ s .logger .WithError (err ).Error ()
511
+ return nil , err
512
+ }
513
+
514
+ err = shimDir .Mkdir ()
515
+ if err != nil {
516
+ err = errors .Wrapf (err , "failed to create VM dir %q" , shimDir .RootPath ())
517
+ s .logger .WithError (err ).Error ()
518
+ return nil , err
519
+ }
520
+
521
+ fcSocketAddress , err := fcShim .FCControlSocketAddress (ctx , vmID )
522
+ if err != nil {
523
+ err = errors .Wrap (err , "failed to obtain shim socket address" )
524
+ s .logger .WithError (err ).Error ()
525
+ return nil , err
526
+ }
527
+
528
+ fcSocket , err := shim .NewSocket (fcSocketAddress )
529
+ if err != nil {
530
+ err = errors .Wrapf (err , "failed to open fccontrol socket at address %q" , fcSocketAddress )
531
+ s .logger .WithError (err ).Error ()
532
+ return nil , err
533
+ }
534
+
535
+ s .fcControlSocket = fcSocket
536
+
537
+ args := []string {
538
+ "-namespace" , ns ,
539
+ "-address" , containerdAddress ,
540
+ }
541
+
542
+ cmd := exec .Command (internal .ShimBinaryName , args ... )
543
+
544
+ // note: The working dir of the shim has an effect on the length of the path
545
+ // needed to specify various unix sockets that the shim uses to communicate
546
+ // with the firecracker VMM and guest agent within. The length of that path
547
+ // has a relatively low limit (usually 108 chars), so modifying the working
548
+ // dir should be done with caution. See internal/vm/dir.go for the path
549
+ // definitions.
550
+ cmd .Dir = shimDir .RootPath ()
551
+
552
+ shimSocketFile , err := shimSocket .File ()
553
+ if err != nil {
554
+ err = errors .Wrap (err , "failed to get shim socket fd" )
555
+ logger .WithError (err ).Error ()
556
+ return nil , err
557
+ }
558
+
559
+ fcSocketFile , err := fcSocket .File ()
560
+ if err != nil {
561
+ err = errors .Wrap (err , "failed to get shim fccontrol socket fd" )
562
+ logger .WithError (err ).Error ()
563
+ return nil , err
564
+ }
565
+
566
+ cmd .ExtraFiles = append (cmd .ExtraFiles , shimSocketFile , fcSocketFile )
567
+ fcSocketFDNum := 2 + len (cmd .ExtraFiles ) // "2 +" because ExtraFiles come after stderr (fd #2)
568
+
569
+ ttrpc := containerdAddress + ".ttrpc"
570
+ cmd .Env = append (os .Environ (),
571
+ fmt .Sprintf ("%s=%s" , ttrpcAddressEnv , ttrpc ),
572
+ fmt .Sprintf ("%s=%s" , internal .VMIDEnvVarKey , vmID ),
573
+ fmt .Sprintf ("%s=%s" , internal .FCSocketFDEnvKey , strconv .Itoa (fcSocketFDNum ))) // TODO remove after containerd is updated to expose ttrpc server to shim
574
+
575
+ cmd .SysProcAttr = & syscall.SysProcAttr {
576
+ Setpgid : true ,
577
+ }
578
+
579
+ // shim stderr is just raw text, so pass it through our logrus formatter first
580
+ cmd .Stderr = logger .WithField ("shim_stream" , "stderr" ).WriterLevel (logrus .ErrorLevel )
581
+ // shim stdout on the other hand is already formatted by logrus, so pass that transparently through to containerd logs
582
+ cmd .Stdout = logger .Logger .Out
583
+
584
+ logger .Debugf ("starting %s" , internal .ShimBinaryName )
585
+
586
+ err = cmd .Start ()
587
+ if err != nil {
588
+ err = errors .Wrap (err , "failed to start shim child process" )
589
+ logger .WithError (err ).Error ()
590
+ return nil , err
591
+ }
592
+
593
+ // make sure to wait after start
594
+ go func () {
595
+ if err := cmd .Wait (); err != nil {
596
+ if exitErr , ok := err .(* exec.ExitError ); ok {
597
+ // shim is usually terminated by cancelling the context
598
+ logger .WithError (exitErr ).Debug ("shim has been terminated" )
599
+ } else {
600
+ logger .WithError (err ).Error ("shim has been unexpectedly terminated" )
601
+ }
602
+ }
603
+
604
+ // Close all Unix abstract sockets.
605
+ if err := shimSocketFile .Close (); err != nil {
606
+ logger .WithError (err ).Errorf ("failed to close %q" , shimSocketFile .Name ())
607
+ }
608
+ if err := fcSocketFile .Close (); err != nil {
609
+ logger .WithError (err ).Errorf ("failed to close %q" , fcSocketFile .Name ())
610
+ }
611
+ }()
612
+
613
+ err = setShimOOMScore (cmd .Process .Pid )
614
+ if err != nil {
615
+ logger .WithError (err ).Error ()
616
+ return nil , err
617
+ }
618
+
619
+ s .addShim (shimSocketAddress , cmd )
620
+
621
+ return cmd , nil
622
+ }
623
+
467
624
// PauseVM Pauses a VM
468
625
func (s * local ) PauseVM (ctx context.Context , req * proto.PauseVMRequest ) (* empty.Empty , error ) {
469
626
client , err := s .shimFirecrackerClient (ctx , req .VMID )
@@ -520,6 +677,18 @@ func (s *local) CreateSnapshot(ctx context.Context, req *proto.CreateSnapshotReq
520
677
521
678
// LoadSnapshot Loads a snapshot of a VM
522
679
func (s * local ) LoadSnapshot (ctx context.Context , req * proto.LoadSnapshotRequest ) (* empty.Empty , error ) {
680
+ ns , err := namespaces .NamespaceRequired (ctx )
681
+ if err != nil {
682
+ err = errors .Wrap (err , "error retrieving namespace of request" )
683
+ s .logger .WithError (err ).Error ()
684
+ return nil , err
685
+ }
686
+
687
+ _ , err = s .loadShim (ctx , ns , req .VMID , s .containerdAddress )
688
+ if err != nil {
689
+ return nil , err
690
+ }
691
+
523
692
client , err := s .shimFirecrackerClient (ctx , req .VMID )
524
693
if err != nil {
525
694
return nil , err
@@ -553,5 +722,36 @@ func (s *local) Offload(ctx context.Context, req *proto.OffloadRequest) (*empty.
553
722
return nil , err
554
723
}
555
724
725
+ s .fcControlSocket .Close ()
726
+
727
+ shimSocketAddress , err := fcShim .SocketAddress (ctx , req .VMID )
728
+ if err != nil {
729
+ err = errors .Wrap (err , "failed to obtain shim socket address" )
730
+ s .logger .WithError (err ).Error ()
731
+ return nil , err
732
+ }
733
+ removeErr := os .RemoveAll (shimSocketAddress )
734
+ if removeErr != nil {
735
+ s .logger .Errorf ("failed to remove shim socket addr file: %v" , removeErr )
736
+ return nil , err
737
+ }
738
+
739
+ fcSocketAddress , err := fcShim .FCControlSocketAddress (ctx , req .VMID )
740
+ if err != nil {
741
+ s .logger .Error ("failed to get FC socket address" )
742
+ return nil , err
743
+ }
744
+ removeErr = os .RemoveAll (fcSocketAddress )
745
+ if removeErr != nil {
746
+ s .logger .Errorf ("failed to remove fc socket addr file: %v" , removeErr )
747
+ return nil , err
748
+ }
749
+
750
+ waitErr := s .waitForShimToExit (ctx , req .VMID , true )
751
+ if waitErr != nil {
752
+ s .logger .Error ("failed to wait for shim to exit on offload" )
753
+ return nil , waitErr
754
+ }
755
+
556
756
return resp , nil
557
757
}
0 commit comments