Skip to content

Commit 5ce573f

Browse files
committed
Implemented snapshot loading capabilities
Signed-off-by: David Son <[email protected]>
1 parent 8d2124d commit 5ce573f

File tree

11 files changed

+463
-8
lines changed

11 files changed

+463
-8
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ vmlinux
66
root-drive.img
77
TestPID.img
88
build/
9-
testdata/fc.stamp
9+
testdata/bin/
1010
testdata/logs/
1111
testdata/ltag
1212
testdata/release-*

Makefile

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ FIRECRACKER_DIR=build/firecracker
2020
FIRECRACKER_TARGET?=x86_64-unknown-linux-musl
2121

2222
FC_TEST_DATA_PATH?=testdata
23+
FC_TEST_BIN_PATH:=$(FC_TEST_DATA_PATH)/bin
2324
FIRECRACKER_BIN=$(FC_TEST_DATA_PATH)/firecracker-main
2425
JAILER_BIN=$(FC_TEST_DATA_PATH)/jailer-main
2526

@@ -35,9 +36,22 @@ release_url=https://github.com/firecracker-microvm/firecracker/releases/download
3536
testdata_objects = \
3637
$(FC_TEST_DATA_PATH)/vmlinux \
3738
$(FC_TEST_DATA_PATH)/root-drive.img \
39+
$(FC_TEST_DATA_PATH)/root-drive-with-ssh.img \
40+
$(FC_TEST_DATA_PATH)/root-drive-ssh-key \
3841
$(FC_TEST_DATA_PATH)/jailer \
3942
$(FC_TEST_DATA_PATH)/firecracker \
40-
$(FC_TEST_DATA_PATH)/ltag
43+
$(FC_TEST_DATA_PATH)/ltag \
44+
$(FC_TEST_BIN_PATH)/ptp \
45+
$(FC_TEST_BIN_PATH)/host-local \
46+
$(FC_TEST_BIN_PATH)/static \
47+
$(FC_TEST_BIN_PATH)/tc-redirect-tap
48+
49+
# Enable pulling of artifacts from S3 instead of building
50+
# TODO: https://github.com/firecracker-microvm/firecracker-go-sdk/issues/418
51+
ifeq ($(GID), 0)
52+
testdata_objects += $(FC_TEST_DATA_PATH)/root-drive-with-ssh.img \
53+
$(FC_TEST_DATA_PATH)/root-drive-ssh-key
54+
endif
4155

4256
testdata_dir = testdata/firecracker.tgz testdata/firecracker_spec-$(firecracker_version).yaml testdata/LICENSE testdata/NOTICE testdata/THIRD-PARTY
4357

@@ -82,6 +96,34 @@ $(FC_TEST_DATA_PATH)/fc.stamp:
8296
$(FC_TEST_DATA_PATH)/root-drive.img:
8397
$(curl) -o $@ https://s3.amazonaws.com/spec.ccfc.min/img/hello/fsfiles/hello-rootfs.ext4
8498

99+
$(FC_TEST_DATA_PATH)/root-drive-with-ssh.img: $(FIRECRACKER_DIR)
100+
$(FIRECRACKER_DIR)/tools/devtool build_rootfs
101+
cp $(FIRECRACKER_DIR)/build/rootfs/bionic.rootfs.ext4 $@
102+
103+
$(FC_TEST_DATA_PATH)/root-drive-ssh-key: $(FC_TEST_DATA_PATH)/root-drive-with-ssh.img
104+
# Need root to move ssh key to testdata location
105+
ifeq ($(GID), 0)
106+
sudo cp $(FIRECRACKER_DIR)/build/rootfs/ssh/id_rsa $@
107+
else
108+
$(warning unable to place ssh key without root permissions)
109+
endif
110+
111+
$(FC_TEST_BIN_PATH)/ptp:
112+
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_BIN_PATH)) \
113+
go get github.com/containernetworking/plugins/plugins/main/ptp
114+
115+
$(FC_TEST_BIN_PATH)/host-local:
116+
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_BIN_PATH)) \
117+
go get github.com/containernetworking/plugins/plugins/ipam/host-local
118+
119+
$(FC_TEST_BIN_PATH)/static:
120+
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_BIN_PATH)) \
121+
go get github.com/containernetworking/plugins/plugins/ipam/static
122+
123+
$(FC_TEST_BIN_PATH)/tc-redirect-tap:
124+
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_BIN_PATH)) \
125+
go get github.com/awslabs/tc-redirect-tap/cmd/tc-redirect-tap
126+
85127
$(FC_TEST_DATA_PATH)/ltag:
86128
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_DATA_PATH)) \
87129
go get github.com/kunalkushwaha/ltag

firecracker.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,23 @@ func (f *Client) CreateSnapshot(ctx context.Context, snapshotParams *models.Snap
281281
return f.client.Operations.CreateSnapshot(params)
282282
}
283283

284+
// LoadSnapshotOpt is a functional option to be used for the
285+
// LoadSnapshot API in setting any additional optional fields.
286+
type LoadSnapshotOpt func(*ops.LoadSnapshotParams)
287+
288+
// LoadSnapshot is a wrapper for the swagger generated client to make
289+
// calling of the API easier.
290+
func (f *Client) LoadSnapshot(ctx context.Context, snapshotParams *models.SnapshotLoadParams, opts ...LoadSnapshotOpt) (*ops.LoadSnapshotNoContent, error) {
291+
params := ops.NewLoadSnapshotParamsWithContext(ctx)
292+
params.SetBody(snapshotParams)
293+
294+
for _, opt := range opts {
295+
opt(params)
296+
}
297+
298+
return f.client.Operations.LoadSnapshot(params)
299+
}
300+
284301
// CreateSyncActionOpt is a functional option to be used for the
285302
// CreateSyncAction API in setting any additional optional fields.
286303
type CreateSyncActionOpt func(*ops.CreateSyncActionParams)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ require (
1818
github.com/sirupsen/logrus v1.8.1
1919
github.com/stretchr/testify v1.8.0
2020
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5
21+
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
2122
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a
2223
)

go.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,8 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
724724
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
725725
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
726726
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
727+
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
728+
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
727729
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
728730
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
729731
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -792,6 +794,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
792794
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
793795
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
794796
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
797+
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
795798
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
796799
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
797800
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=

handlers.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ const (
3434
LinkFilesToRootFSHandlerName = "fcinit.LinkFilesToRootFS"
3535
SetupNetworkHandlerName = "fcinit.SetupNetwork"
3636
SetupKernelArgsHandlerName = "fcinit.SetupKernelArgs"
37-
CreateBalloonHandlerName = "fcint.CreateBalloon"
37+
CreateBalloonHandlerName = "fcinit.CreateBalloon"
38+
LoadSnapshotHandlerName = "fcinit.LoadSnapshot"
3839

3940
ValidateCfgHandlerName = "validate.Cfg"
4041
ValidateJailerCfgHandlerName = "validate.JailerCfg"
@@ -280,6 +281,16 @@ func NewCreateBalloonHandler(amountMib int64, deflateOnOom bool, StatsPollingInt
280281
}
281282
}
282283

284+
// LoadSnapshotHandler is a named handler that loads a snapshot
285+
// from the specified filepath
286+
var LoadSnapshotHandler = Handler{
287+
Name: LoadSnapshotHandlerName,
288+
Fn: func(ctx context.Context, m *Machine) error {
289+
snapshot := m.Cfg.Snapshot
290+
return m.loadSnapshot(ctx, snapshot.MemFilePath, snapshot.SnapshotPath, snapshot.Opts...)
291+
},
292+
}
293+
283294
var defaultFcInitHandlerList = HandlerList{}.Append(
284295
SetupNetworkHandler,
285296
SetupKernelArgsHandler,
@@ -294,6 +305,15 @@ var defaultFcInitHandlerList = HandlerList{}.Append(
294305
ConfigMmdsHandler,
295306
)
296307

308+
var loadSnapshotHandlerList = HandlerList{}.Append(
309+
SetupNetworkHandler,
310+
StartVMMHandler,
311+
CreateLogFilesHandler,
312+
BootstrapLoggingHandler,
313+
LoadSnapshotHandler,
314+
AddVsocksHandler,
315+
)
316+
297317
var defaultValidationHandlerList = HandlerList{}.Append(
298318
NetworkConfigValidationHandler,
299319
)

machine.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ type Config struct {
151151
// It is possible to use a valid IPv4 link-local address (169.254.0.0/16).
152152
// If not provided, the default address (169.254.169.254) will be used.
153153
MmdsAddress net.IP
154+
155+
// Configuration for snapshot loading
156+
Snapshot SnapshotConfig
157+
}
158+
159+
func (cfg *Config) hasSnapshot() bool {
160+
return cfg.Snapshot.MemFilePath != "" || cfg.Snapshot.SnapshotPath != ""
154161
}
155162

156163
// Validate will ensure that the required fields are set and that
@@ -381,7 +388,7 @@ func NewMachine(ctx context.Context, cfg Config, opts ...Opt) (*Machine, error)
381388
// handlers succeed, then this will start the VMM instance.
382389
// Start may only be called once per Machine. Subsequent calls will return
383390
// ErrAlreadyStarted.
384-
func (m *Machine) Start(ctx context.Context) error {
391+
func (m *Machine) Start(ctx context.Context, opts ...StartOpt) error {
385392
m.logger.Debug("Called Machine.Start()")
386393
alreadyStarted := true
387394
m.startOnce.Do(func() {
@@ -402,6 +409,10 @@ func (m *Machine) Start(ctx context.Context) error {
402409
}
403410
}()
404411

412+
for _, opt := range opts {
413+
opt(m)
414+
}
415+
405416
err = m.Handlers.Run(ctx, m)
406417
if err != nil {
407418
return err
@@ -826,6 +837,10 @@ func (m *Machine) UpdateGuestNetworkInterfaceRateLimit(ctx context.Context, ifac
826837

827838
// attachDrive attaches a secondary block device
828839
func (m *Machine) attachDrive(ctx context.Context, dev models.Drive) error {
840+
if m.Cfg.hasSnapshot() {
841+
return nil
842+
}
843+
829844
hostPath := StringValue(dev.PathOnHost)
830845
m.logger.Infof("Attaching drive %s, slot %s, root %t.", hostPath, StringValue(dev.DriveID), BoolValue(dev.IsRootDevice))
831846
respNoContent, err := m.client.PutGuestDriveByID(ctx, StringValue(dev.DriveID), &dev)
@@ -854,6 +869,10 @@ func (m *Machine) addVsock(ctx context.Context, dev VsockDevice) error {
854869
}
855870

856871
func (m *Machine) startInstance(ctx context.Context) error {
872+
if m.Cfg.hasSnapshot() {
873+
return nil
874+
}
875+
857876
action := models.InstanceActionInfoActionTypeInstanceStart
858877
info := models.InstanceActionInfo{
859878
ActionType: &action,
@@ -1105,6 +1124,21 @@ func (m *Machine) CreateSnapshot(ctx context.Context, memFilePath, snapshotPath
11051124
return nil
11061125
}
11071126

1127+
// loadSnapshot loads a snapshot of the VM
1128+
func (m *Machine) loadSnapshot(ctx context.Context, memFilePath, snapshotPath string, opts ...LoadSnapshotOpt) error {
1129+
snapshotParams := &models.SnapshotLoadParams{
1130+
MemFilePath: String(memFilePath),
1131+
SnapshotPath: String(snapshotPath),
1132+
}
1133+
1134+
if _, err := m.client.LoadSnapshot(ctx, snapshotParams, opts...); err != nil {
1135+
return fmt.Errorf("failed to load a snapshot for VM: %v", err)
1136+
}
1137+
1138+
m.logger.Debug("snapshot loaded successfully")
1139+
return nil
1140+
}
1141+
11081142
// CreateBalloon creates a balloon device if one does not exist
11091143
func (m *Machine) CreateBalloon(ctx context.Context, amountMib int64, deflateOnOom bool, statsPollingIntervals int64, opts ...PutBalloonOpt) error {
11101144
balloon := models.Balloon{

0 commit comments

Comments
 (0)