|
14 | 14 | package main
|
15 | 15 |
|
16 | 16 | import (
|
| 17 | + "bytes" |
17 | 18 | "context"
|
18 | 19 | "encoding/json"
|
19 | 20 | "fmt"
|
20 | 21 | "math"
|
21 | 22 | "net"
|
| 23 | + "net/http" |
22 | 24 | "os"
|
23 | 25 | "runtime/debug"
|
24 | 26 | "strconv"
|
@@ -64,6 +66,7 @@ import (
|
64 | 66 | drivemount "github.com/firecracker-microvm/firecracker-containerd/proto/service/drivemount/ttrpc"
|
65 | 67 | fccontrolTtrpc "github.com/firecracker-microvm/firecracker-containerd/proto/service/fccontrol/ttrpc"
|
66 | 68 | ioproxy "github.com/firecracker-microvm/firecracker-containerd/proto/service/ioproxy/ttrpc"
|
| 69 | + "github.com/tv42/httpunix" |
67 | 70 | )
|
68 | 71 |
|
69 | 72 | func init() {
|
@@ -157,6 +160,9 @@ type service struct {
|
157 | 160 | // fifos have stdio FIFOs containerd passed to the shim. The key is [taskID][execID].
|
158 | 161 | fifos map[string]map[string]cio.Config
|
159 | 162 | fifosMu sync.Mutex
|
| 163 | + |
| 164 | + // httpControlClient is to send pause/resume/snapshot commands to the microVM |
| 165 | + httpControlClient *http.Client |
160 | 166 | }
|
161 | 167 |
|
162 | 168 | func shimOpts(shimCtx context.Context) (*shim.Opts, error) {
|
@@ -620,6 +626,8 @@ func (s *service) createVM(requestCtx context.Context, request *proto.CreateVMRe
|
620 | 626 | return err
|
621 | 627 | }
|
622 | 628 |
|
| 629 | + s.createHTTPControlClient() |
| 630 | + |
623 | 631 | s.logger.Info("successfully started the VM")
|
624 | 632 | return nil
|
625 | 633 | }
|
@@ -672,42 +680,6 @@ func (s *service) StopVM(requestCtx context.Context, request *proto.StopVMReques
|
672 | 680 | return &empty.Empty{}, nil
|
673 | 681 | }
|
674 | 682 |
|
675 |
| -// ResumeVM resumes a VM |
676 |
| -func (s *service) ResumeVM(ctx context.Context, req *proto.ResumeVMRequest) (*empty.Empty, error) { |
677 |
| - defer logPanicAndDie(s.logger) |
678 |
| - |
679 |
| - err := s.waitVMReady() |
680 |
| - if err != nil { |
681 |
| - s.logger.WithError(err).Error() |
682 |
| - return nil, err |
683 |
| - } |
684 |
| - |
685 |
| - if err := s.machine.ResumeVM(ctx); err != nil { |
686 |
| - s.logger.WithError(err).Error() |
687 |
| - return nil, err |
688 |
| - } |
689 |
| - |
690 |
| - return &empty.Empty{}, nil |
691 |
| -} |
692 |
| - |
693 |
| -// PauseVM pauses a VM |
694 |
| -func (s *service) PauseVM(ctx context.Context, req *proto.PauseVMRequest) (*empty.Empty, error) { |
695 |
| - defer logPanicAndDie(s.logger) |
696 |
| - |
697 |
| - err := s.waitVMReady() |
698 |
| - if err != nil { |
699 |
| - s.logger.WithError(err).Error() |
700 |
| - return nil, err |
701 |
| - } |
702 |
| - |
703 |
| - if err := s.machine.PauseVM(ctx); err != nil { |
704 |
| - s.logger.WithError(err).Error() |
705 |
| - return nil, err |
706 |
| - } |
707 |
| - |
708 |
| - return &empty.Empty{}, nil |
709 |
| -} |
710 |
| - |
711 | 683 | // GetVMInfo returns metadata for the VM being managed by this shim. If the VM has not been created yet, this
|
712 | 684 | // method will wait for up to a hardcoded timeout for it to exist, returning an error if the timeout is reached.
|
713 | 685 | func (s *service) GetVMInfo(requestCtx context.Context, request *proto.GetVMInfoRequest) (*proto.GetVMInfoResponse, error) {
|
@@ -1735,3 +1707,68 @@ func (s *service) monitorVMExit() {
|
1735 | 1707 | s.logger.WithError(err).Error("failed to clean up the VM")
|
1736 | 1708 | }
|
1737 | 1709 | }
|
| 1710 | + |
| 1711 | +func (s *service) createHTTPControlClient() { |
| 1712 | + u := &httpunix.Transport{ |
| 1713 | + DialTimeout: 100 * time.Millisecond, |
| 1714 | + RequestTimeout: 10 * time.Second, |
| 1715 | + ResponseHeaderTimeout: 10 * time.Second, |
| 1716 | + } |
| 1717 | + u.RegisterLocation("firecracker", s.shimDir.FirecrackerSockPath()) |
| 1718 | + |
| 1719 | + t := &http.Transport{} |
| 1720 | + t.RegisterProtocol(httpunix.Scheme, u) |
| 1721 | + |
| 1722 | + var client = http.Client{ |
| 1723 | + Transport: t, |
| 1724 | + } |
| 1725 | + |
| 1726 | + s.httpControlClient = &client |
| 1727 | +} |
| 1728 | + |
| 1729 | +func formResumeReq() (*http.Request, error) { |
| 1730 | + var req *http.Request |
| 1731 | + |
| 1732 | + data := map[string]string{ |
| 1733 | + "state": "Resumed", |
| 1734 | + } |
| 1735 | + json, err := json.Marshal(data) |
| 1736 | + if err != nil { |
| 1737 | + logrus.WithError(err).Error("Failed to marshal json data") |
| 1738 | + return nil, err |
| 1739 | + } |
| 1740 | + |
| 1741 | + req, err = http.NewRequest("PATCH", "http+unix://firecracker/vm", bytes.NewBuffer(json)) |
| 1742 | + if err != nil { |
| 1743 | + logrus.WithError(err).Error("Failed to create new HTTP request in formResumeReq") |
| 1744 | + return nil, err |
| 1745 | + } |
| 1746 | + req.Header.Add("accept", "application/json") |
| 1747 | + req.Header.Add("Content-Type", "application/json") |
| 1748 | + |
| 1749 | + return req, nil |
| 1750 | +} |
| 1751 | + |
| 1752 | +func formPauseReq() (*http.Request, error) { |
| 1753 | + var req *http.Request |
| 1754 | + |
| 1755 | + data := map[string]string{ |
| 1756 | + "state": "Paused", |
| 1757 | + } |
| 1758 | + json, err := json.Marshal(data) |
| 1759 | + if err != nil { |
| 1760 | + logrus.WithError(err).Error("Failed to marshal json data") |
| 1761 | + return nil, err |
| 1762 | + } |
| 1763 | + |
| 1764 | + req, err = http.NewRequest("PATCH", "http+unix://firecracker/vm", bytes.NewBuffer(json)) |
| 1765 | + if err != nil { |
| 1766 | + logrus.WithError(err).Error("Failed to create new HTTP request in formPauseReq") |
| 1767 | + return nil, err |
| 1768 | + } |
| 1769 | + req.Header.Add("accept", "application/json") |
| 1770 | + req.Header.Add("Content-Type", "application/json") |
| 1771 | + |
| 1772 | + return req, nil |
| 1773 | +} |
| 1774 | + |
0 commit comments