Skip to content

LoadSnapshot #395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions firecracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,23 @@ func (f *Client) CreateSnapshot(ctx context.Context, snapshotParams *models.Snap
return f.client.Operations.CreateSnapshot(params)
}

// LoadSnapshotOpt is a functional option to be used for the
// LoadSnapshot API in setting any additional optional fields.
type LoadSnapshotOpt func(*ops.LoadSnapshotParams)

// LoadSnapshot is a wrapper for the swagger generated client to make
// calling of the API easier.
func (f *Client) LoadSnapshot(ctx context.Context, snapshotParams *models.SnapshotLoadParams, opts ...LoadSnapshotOpt) (*ops.LoadSnapshotNoContent, error) {
params := ops.NewLoadSnapshotParamsWithContext(ctx)
params.SetBody(snapshotParams)

for _, opt := range opts {
opt(params)
}

return f.client.Operations.LoadSnapshot(params)
}

// CreateSyncActionOpt is a functional option to be used for the
// CreateSyncAction API in setting any additional optional fields.
type CreateSyncActionOpt func(*ops.CreateSyncActionParams)
Expand Down
31 changes: 30 additions & 1 deletion machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ type Config struct {
// It is possible to use a valid IPv4 link-local address (169.254.0.0/16).
// If not provided, the default address (169.254.169.254) will be used.
MmdsAddress net.IP

Snapshot SnapshotConfig
}

func (cfg *Config) hasSnapshot() bool {
return cfg.Snapshot.MemFilePath != "" || cfg.Snapshot.SnapshotPath != ""
}

// Validate will ensure that the required fields are set and that
Expand Down Expand Up @@ -380,7 +386,7 @@ func NewMachine(ctx context.Context, cfg Config, opts ...Opt) (*Machine, error)
// handlers succeed, then this will start the VMM instance.
// Start may only be called once per Machine. Subsequent calls will return
// ErrAlreadyStarted.
func (m *Machine) Start(ctx context.Context) error {
func (m *Machine) Start(ctx context.Context, opts ...Opt) error {
m.logger.Debug("Called Machine.Start()")
alreadyStarted := true
m.startOnce.Do(func() {
Expand All @@ -401,6 +407,10 @@ func (m *Machine) Start(ctx context.Context) error {
}
}()

for _, opt := range opts {
opt(m)
}

err = m.Handlers.Run(ctx, m)
if err != nil {
return err
Expand Down Expand Up @@ -718,6 +728,17 @@ func (m *Machine) captureFifoToFileWithChannel(ctx context.Context, logger *log.
}

func (m *Machine) createMachine(ctx context.Context) error {
ss := m.Cfg.Snapshot
if ss.SnapshotPath != "" || ss.MemFilePath != "" {
_, err := m.client.LoadSnapshot(ctx, &models.SnapshotLoadParams{
SnapshotPath: String(ss.SnapshotPath),
MemFilePath: String(ss.MemFilePath),
EnableDiffSnapshots: ss.EnableDiffSnapshots,
ResumeVM: ss.ResumeVM,
})
return err
}

resp, err := m.client.PutMachineConfiguration(ctx, &m.Cfg.MachineCfg)
if err != nil {
m.logger.Errorf("PutMachineConfiguration returned %s", resp.Error())
Expand All @@ -734,6 +755,10 @@ func (m *Machine) createMachine(ctx context.Context) error {
}

func (m *Machine) createBootSource(ctx context.Context, imagePath, initrdPath, kernelArgs string) error {
if m.Cfg.hasSnapshot() {
return nil
}

bsrc := models.BootSource{
KernelImagePath: &imagePath,
InitrdPath: initrdPath,
Expand Down Expand Up @@ -841,6 +866,10 @@ func (m *Machine) addVsock(ctx context.Context, dev VsockDevice) error {
}

func (m *Machine) startInstance(ctx context.Context) error {
if m.Cfg.hasSnapshot() {
return nil
}

action := models.InstanceActionInfoActionTypeInstanceStart
info := models.InstanceActionInfo{
ActionType: &action,
Expand Down
77 changes: 77 additions & 0 deletions machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ var (
testBalloonDeflateOnOom = true
testStatsPollingIntervals = int64(1)
testNewStatsPollingIntervals = int64(6)

// How long to wait for the socket to appear.
firecrackerSocketWait = int64(10)
)

func envOrDefault(k, empty string) string {
Expand Down Expand Up @@ -1728,6 +1731,80 @@ func TestCreateSnapshot(t *testing.T) {
}
}

func TestLoadSnapshot(t *testing.T) {
fctesting.RequiresKVM(t)
fctesting.RequiresRoot(t)

ctx := context.Background()

dir, err := ioutil.TempDir("", t.Name())
require.NoError(t, err)
defer os.RemoveAll(dir)

// Set snap and mem paths
socketPath := filepath.Join(dir, fsSafeTestName.Replace(t.Name()))
snapPath := socketPath + "SnapFile"
memPath := socketPath + "MemFile"
defer os.Remove(socketPath)
defer os.Remove(snapPath)
defer os.Remove(memPath)

// Tee logs for validation:
var logBuffer bytes.Buffer
machineLogger := logrus.New()
machineLogger.Out = io.MultiWriter(os.Stderr, &logBuffer)

// Create a snapshot
{
cfg := createValidConfig(t, socketPath+".create")
m, err := NewMachine(ctx, cfg, func(m *Machine) {
// Rewriting m.cmd partially wouldn't work since Cmd has
// some unexported members
args := m.cmd.Args[1:]
m.cmd = exec.Command(getFirecrackerBinaryPath(), args...)
}, WithLogger(logrus.NewEntry(machineLogger)))
require.NoError(t, err)

err = m.Start(ctx)
require.NoError(t, err)

err = m.PauseVM(ctx)
require.NoError(t, err)

err = m.CreateSnapshot(ctx, memPath, snapPath)
require.NoError(t, err)

err = m.StopVMM()
require.NoError(t, err)
}

// Load a snapshot
{
cfg := Config{
DisableValidation: true,
SocketPath: socketPath + ".load",
Snapshot: SnapshotConfig{SnapshotPath: snapPath, MemFilePath: memPath},
}
m, err := NewMachine(ctx, cfg, func(m *Machine) {
// Rewriting m.cmd partially wouldn't work since Cmd has
// some unexported members
args := m.cmd.Args[1:]
m.cmd = exec.Command(getFirecrackerBinaryPath(), args...)
}, WithLogger(logrus.NewEntry(machineLogger)))
require.NoError(t, err)

err = m.Start(ctx)
require.NoError(t, err)

err = m.ResumeVM(ctx)
require.NoError(t, err)

err = m.StopVMM()
require.NoError(t, err)
}

}

func testCreateBalloon(ctx context.Context, t *testing.T, m *Machine) {
if err := m.CreateBalloon(ctx, testBalloonMemory, testBalloonDeflateOnOom, testStatsPollingIntervals); err != nil {
t.Errorf("Create balloon device failed from testAttachBalloon: %s", err)
Expand Down
2 changes: 1 addition & 1 deletion machineiface.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var _ MachineIface = (*Machine)(nil)
// MachineIface can be used for mocking and testing of the Machine. The Machine
// is subject to change, meaning this interface would change.
type MachineIface interface {
Start(context.Context) error
Start(context.Context, ...Opt) error
StopVMM() error
Shutdown(context.Context) error
Wait(context.Context) error
Expand Down
21 changes: 21 additions & 0 deletions snapshot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package firecracker

type SnapshotConfig struct {
MemFilePath string
SnapshotPath string
EnableDiffSnapshots bool
ResumeVM bool
}