Skip to content

Commit 27af710

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

File tree

13 files changed

+538
-9
lines changed

13 files changed

+538
-9
lines changed

.buildkite/hooks/pre-exit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/bin/bash
22
set -euo pipefail
33

4-
find . -user root -print0 | xargs -0 sudo rm -rf '{}'
4+
sudo chown -hR "${USER}:${USER}" .

.buildkite/pipeline.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ steps:
7878
- ln -s /var/lib/fc-ci/rootfs.ext4 ${FC_TEST_DATA_PATH}/root-drive.img
7979
# Download Firecracker and its jailer.
8080
- make deps
81+
# Build a rootfs with SSH enabled.
82+
- sudo -E FC_TEST_DATA_PATH=${FC_TEST_DATA_PATH} make ${FC_TEST_DATA_PATH}/root-drive-ssh-key
83+
- stat ${FC_TEST_DATA_PATH}/root-drive-ssh-key ${FC_TEST_DATA_PATH}/root-drive-with-ssh.img
8184
agents:
8285
queue: "${BUILDKITE_AGENT_META_DATA_QUEUE:-default}"
8386
distro: "${BUILDKITE_AGENT_META_DATA_DISTRO}"
@@ -110,7 +113,7 @@ steps:
110113

111114
- label: ':hammer: root tests'
112115
commands:
113-
- "sudo PATH=$PATH FC_TEST_TAP=fc-root-tap${BUILDKITE_BUILD_NUMBER} FC_TEST_DATA_PATH=${FC_TEST_DATA_PATH} make test EXTRAGOARGS='-v -count=1 -race' DISABLE_ROOT_TESTS="
116+
- "sudo -E PATH=$PATH FC_TEST_TAP=fc-root-tap${BUILDKITE_BUILD_NUMBER} FC_TEST_DATA_PATH=${FC_TEST_DATA_PATH} make test EXTRAGOARGS='-v -count=1 -race' DISABLE_ROOT_TESTS="
114117
agents:
115118
queue: "${BUILDKITE_AGENT_META_DATA_QUEUE:-default}"
116119
distro: "${BUILDKITE_AGENT_META_DATA_DISTRO}"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ root-drive.img
77
TestPID.img
88
build/
99
testdata/fc.stamp
10+
testdata/bin/
1011
testdata/logs/
1112
testdata/ltag
1213
testdata/release-*

Makefile

Lines changed: 40 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

@@ -37,7 +38,17 @@ $(FC_TEST_DATA_PATH)/vmlinux \
3738
$(FC_TEST_DATA_PATH)/root-drive.img \
3839
$(FC_TEST_DATA_PATH)/jailer \
3940
$(FC_TEST_DATA_PATH)/firecracker \
40-
$(FC_TEST_DATA_PATH)/ltag
41+
$(FC_TEST_DATA_PATH)/ltag \
42+
$(FC_TEST_BIN_PATH)/ptp \
43+
$(FC_TEST_BIN_PATH)/host-local \
44+
$(FC_TEST_BIN_PATH)/static \
45+
$(FC_TEST_BIN_PATH)/tc-redirect-tap
46+
47+
# Enable pulling of artifacts from S3 instead of building
48+
# TODO: https://github.com/firecracker-microvm/firecracker-go-sdk/issues/418
49+
ifeq ($(GID), 0)
50+
testdata_objects += $(FC_TEST_DATA_PATH)/root-drive-with-ssh.img $(FC_TEST_DATA_PATH)/root-drive-ssh-key
51+
endif
4152

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

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

96+
$(FC_TEST_DATA_PATH)/root-drive-ssh-key $(FC_TEST_DATA_PATH)/root-drive-with-ssh.img:
97+
# Need root to move ssh key to testdata location
98+
ifeq ($(GID), 0)
99+
$(MAKE) $(FIRECRACKER_DIR)
100+
$(FIRECRACKER_DIR)/tools/devtool build_rootfs
101+
cp $(FIRECRACKER_DIR)/build/rootfs/bionic.rootfs.ext4 $(FC_TEST_DATA_PATH)/root-drive-with-ssh.img
102+
cp $(FIRECRACKER_DIR)/build/rootfs/ssh/id_rsa $(FC_TEST_DATA_PATH)/root-drive-ssh-key
103+
rm -rf $(FIRECRACKER_DIR)
104+
else
105+
$(error unable to place ssh key without root permissions)
106+
endif
107+
108+
$(FC_TEST_BIN_PATH)/ptp:
109+
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_BIN_PATH)) \
110+
go get github.com/containernetworking/plugins/plugins/main/ptp
111+
112+
$(FC_TEST_BIN_PATH)/host-local:
113+
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_BIN_PATH)) \
114+
go get github.com/containernetworking/plugins/plugins/ipam/host-local
115+
116+
$(FC_TEST_BIN_PATH)/static:
117+
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_BIN_PATH)) \
118+
go get github.com/containernetworking/plugins/plugins/ipam/static
119+
120+
$(FC_TEST_BIN_PATH)/tc-redirect-tap:
121+
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_BIN_PATH)) \
122+
go get github.com/awslabs/tc-redirect-tap/cmd/tc-redirect-tap
123+
85124
$(FC_TEST_DATA_PATH)/ltag:
86125
GO111MODULE=off GOBIN=$(abspath $(FC_TEST_DATA_PATH)) \
87126
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: 4 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=
@@ -886,6 +889,7 @@ golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr
886889
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
887890
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
888891
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
892+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
889893
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
890894
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
891895
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

handlers.go

Lines changed: 20 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,15 @@ 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+
return m.loadSnapshot(ctx, &m.Cfg.Snapshot)
290+
},
291+
}
292+
283293
var defaultFcInitHandlerList = HandlerList{}.Append(
284294
SetupNetworkHandler,
285295
SetupKernelArgsHandler,
@@ -294,6 +304,15 @@ var defaultFcInitHandlerList = HandlerList{}.Append(
294304
ConfigMmdsHandler,
295305
)
296306

307+
var loadSnapshotHandlerList = HandlerList{}.Append(
308+
SetupNetworkHandler,
309+
StartVMMHandler,
310+
CreateLogFilesHandler,
311+
BootstrapLoggingHandler,
312+
LoadSnapshotHandler,
313+
AddVsocksHandler,
314+
)
315+
297316
var defaultValidationHandlerList = HandlerList{}.Append(
298317
NetworkConfigValidationHandler,
299318
)

machine.go

Lines changed: 33 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
@@ -854,6 +865,10 @@ func (m *Machine) addVsock(ctx context.Context, dev VsockDevice) error {
854865
}
855866

856867
func (m *Machine) startInstance(ctx context.Context) error {
868+
if m.Cfg.hasSnapshot() {
869+
return nil
870+
}
871+
857872
action := models.InstanceActionInfoActionTypeInstanceStart
858873
info := models.InstanceActionInfo{
859874
ActionType: &action,
@@ -1105,6 +1120,23 @@ func (m *Machine) CreateSnapshot(ctx context.Context, memFilePath, snapshotPath
11051120
return nil
11061121
}
11071122

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

0 commit comments

Comments
 (0)