Skip to content

Commit 661ff94

Browse files
plamenmpetrovustiugov
authored andcommitted
kill shim functionality
firecracker update Signed-off-by: Plamen Petrov <[email protected]>
1 parent 0fe7d17 commit 661ff94

File tree

4 files changed

+205
-48
lines changed

4 files changed

+205
-48
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,8 @@ demo-network: install-cni-bins $(FCNET_CONFIG)
278278
# Firecracker submodule
279279
##########################
280280
.PHONY: firecracker
281-
firecracker: $(FIRECRACKER_BIN) $(JAILER_BIN)
282-
281+
firecracker:
282+
_submodules/firecracker/tools/devtool build --release
283283
.PHONY: install-firecracker
284284
install-firecracker: firecracker
285285
install -D -o root -g root -m755 -t $(INSTALLROOT)/bin $(FIRECRACKER_BIN)

_submodules/firecracker

Submodule firecracker updated 190 files

firecracker-control/local.go

Lines changed: 202 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ type local struct {
7171

7272
processesMu sync.Mutex
7373
processes map[string]int32
74+
75+
fcControlSocket *net.UnixListener
7476
}
7577

7678
func newLocal(ic *plugin.InitContext) (*local, error) {
@@ -243,7 +245,7 @@ func (s *local) StopVM(requestCtx context.Context, req *proto.StopVMRequest) (*e
243245
defer client.Close()
244246

245247
resp, shimErr := client.StopVM(requestCtx, req)
246-
waitErr := s.waitForShimToExit(requestCtx, req.VMID)
248+
waitErr := s.waitForShimToExit(requestCtx, req.VMID, false)
247249

248250
// Assuming the shim is returning containerd's error code, return the error as is if possible.
249251
if waitErr == nil {
@@ -252,7 +254,7 @@ func (s *local) StopVM(requestCtx context.Context, req *proto.StopVMRequest) (*e
252254
return resp, multierror.Append(shimErr, waitErr).ErrorOrNil()
253255
}
254256

255-
func (s *local) waitForShimToExit(ctx context.Context, vmID string) error {
257+
func (s *local) waitForShimToExit(ctx context.Context, vmID string, killShim bool) error {
256258
socketAddr, err := fcShim.SocketAddress(ctx, vmID)
257259
if err != nil {
258260
return err
@@ -267,6 +269,17 @@ func (s *local) waitForShimToExit(ctx context.Context, vmID string) error {
267269
}
268270
defer delete(s.processes, socketAddr)
269271

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+
270283
return internal.WaitForPidToExit(ctx, stopVMInterval, pid)
271284
}
272285

@@ -464,6 +477,150 @@ func setShimOOMScore(shimPid int) error {
464477
return nil
465478
}
466479

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+
467624
// PauseVM Pauses a VM
468625
func (s *local) PauseVM(ctx context.Context, req *proto.PauseVMRequest) (*empty.Empty, error) {
469626
client, err := s.shimFirecrackerClient(ctx, req.VMID)
@@ -520,6 +677,18 @@ func (s *local) CreateSnapshot(ctx context.Context, req *proto.CreateSnapshotReq
520677

521678
// LoadSnapshot Loads a snapshot of a VM
522679
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+
523692
client, err := s.shimFirecrackerClient(ctx, req.VMID)
524693
if err != nil {
525694
return nil, err
@@ -553,5 +722,36 @@ func (s *local) Offload(ctx context.Context, req *proto.OffloadRequest) (*empty.
553722
return nil, err
554723
}
555724

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+
556756
return resp, nil
557757
}

runtime/service.go

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -811,24 +811,6 @@ func (s *service) LoadSnapshot(ctx context.Context, req *proto.LoadSnapshotReque
811811
return nil, err
812812
}
813813

814-
// Workaround for https://github.com/firecracker-microvm/firecracker/issues/1974
815-
patchDriveReq, err := formPatchDriveReq("MN2HE43UOVRDA", s.taskDrivePathOnHost)
816-
if err != nil {
817-
s.logger.WithError(err).Error("Failed to create patch drive request")
818-
return nil, err
819-
}
820-
821-
resp, err = s.httpControlClient.Do(patchDriveReq)
822-
if err != nil {
823-
s.logger.WithError(err).Error("Failed to send patch drive request")
824-
return nil, err
825-
}
826-
if !strings.Contains(resp.Status, "204") {
827-
s.logger.WithError(err).Error("Failed to patch drive")
828-
s.logger.WithError(err).Errorf("Status of request: %s", resp.Status)
829-
return nil, err
830-
}
831-
832814
return &empty.Empty{}, nil
833815
}
834816

@@ -1643,31 +1625,6 @@ func formCreateSnapReq(snapshotPath, memPath string) (*http.Request, error) {
16431625
return req, nil
16441626
}
16451627

1646-
func formPatchDriveReq(driveID, pathOnHost string) (*http.Request, error) {
1647-
var req *http.Request
1648-
1649-
data := map[string]string{
1650-
"drive_id": driveID,
1651-
"path_on_host": pathOnHost,
1652-
}
1653-
json, err := json.Marshal(data)
1654-
if err != nil {
1655-
logrus.WithError(err).Error("Failed to marshal json data")
1656-
return nil, err
1657-
}
1658-
1659-
req, err = http.NewRequest("PATCH", fmt.Sprintf("http+unix://firecracker/drives/%s", driveID), bytes.NewBuffer(json))
1660-
if err != nil {
1661-
logrus.WithError(err).Error("Failed to create new HTTP request in formPauseReq")
1662-
return nil, err
1663-
}
1664-
1665-
req.Header.Add("accept", "application/json")
1666-
req.Header.Add("Content-Type", "application/json")
1667-
1668-
return req, nil
1669-
}
1670-
16711628
func (s *service) startFirecrackerProcess() error {
16721629
firecPath, err := exec.LookPath("firecracker")
16731630
if err != nil {

0 commit comments

Comments
 (0)