Skip to content
Merged
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
2 changes: 0 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,12 @@ test-linux: ensure-ch-binaries ensure-firecracker-binaries ensure-caddy-binaries
if [ -n "$(TEST)" ]; then \
echo "Running specific test: $(TEST)"; \
sudo env "PATH=$$TEST_PATH" "DOCKER_CONFIG=$${DOCKER_CONFIG:-$$HOME/.docker}" "CI=$${CI:-}" \
"HYPEMAN_TEST_NETWORK_TMPDIR=$${HYPEMAN_TEST_NETWORK_TMPDIR:-}" \
"HYPEMAN_TEST_PREWARM_DIR=$${HYPEMAN_TEST_PREWARM_DIR:-}" \
"HYPEMAN_TEST_PREWARM_STRICT=$${HYPEMAN_TEST_PREWARM_STRICT:-}" \
"HYPEMAN_TEST_REGISTRY=$${HYPEMAN_TEST_REGISTRY:-}" \
go test -tags containers_image_openpgp -run=$(TEST) $$VERBOSE_FLAG -timeout=$(TEST_TIMEOUT) ./...; \
else \
sudo env "PATH=$$TEST_PATH" "DOCKER_CONFIG=$${DOCKER_CONFIG:-$$HOME/.docker}" "CI=$${CI:-}" \
"HYPEMAN_TEST_NETWORK_TMPDIR=$${HYPEMAN_TEST_NETWORK_TMPDIR:-}" \
"HYPEMAN_TEST_PREWARM_DIR=$${HYPEMAN_TEST_PREWARM_DIR:-}" \
"HYPEMAN_TEST_PREWARM_STRICT=$${HYPEMAN_TEST_PREWARM_STRICT:-}" \
"HYPEMAN_TEST_REGISTRY=$${HYPEMAN_TEST_REGISTRY:-}" \
Expand Down
4 changes: 1 addition & 3 deletions cmd/api/api/snapshots.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,7 @@ func (s *ApiService) ForkSnapshot(ctx context.Context, request oapi.ForkSnapshot
return oapi.ForkSnapshot400JSONResponse{Code: "invalid_request", Message: "request body is required"}, nil
}

domainReq := instances.ForkSnapshotRequest{
Name: request.Body.Name,
}
domainReq := instances.ForkSnapshotRequest{Name: request.Body.Name}
if request.Body.TargetState != nil {
domainReq.TargetState = instances.State(*request.Body.TargetState)
}
Expand Down
57 changes: 0 additions & 57 deletions cmd/api/api/snapshots_test.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,14 @@
package api

import (
"context"
"testing"
"time"

"github.com/kernel/hypeman/lib/hypervisor"
"github.com/kernel/hypeman/lib/instances"
"github.com/kernel/hypeman/lib/oapi"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type captureForkSnapshotManager struct {
instances.Manager
lastID string
lastReq *instances.ForkSnapshotRequest
result *instances.Instance
err error
}

func (m *captureForkSnapshotManager) ForkSnapshot(ctx context.Context, snapshotID string, req instances.ForkSnapshotRequest) (*instances.Instance, error) {
reqCopy := req
m.lastID = snapshotID
m.lastReq = &reqCopy
if m.err != nil {
return nil, m.err
}
return m.result, nil
}

func TestSnapshotScheduleToOAPIPreservesZeroMaxCount(t *testing.T) {
t.Parallel()

Expand All @@ -51,39 +30,3 @@ func TestSnapshotScheduleToOAPIPreservesZeroMaxCount(t *testing.T) {
require.NotNil(t, out.Retention.MaxAge)
assert.Equal(t, "24h0m0s", *out.Retention.MaxAge)
}

func TestForkSnapshotSuccess(t *testing.T) {
t.Parallel()
svc := newTestService(t)

forked := &instances.Instance{
StoredMetadata: instances.StoredMetadata{
Id: "forked-instance",
Name: "forked-instance",
Image: "docker.io/library/alpine:latest",
CreatedAt: time.Now(),
HypervisorType: hypervisor.TypeFirecracker,
},
State: instances.StateRunning,
}
mockMgr := &captureForkSnapshotManager{
Manager: svc.InstanceManager,
result: forked,
}
svc.InstanceManager = mockMgr

resp, err := svc.ForkSnapshot(ctx(), oapi.ForkSnapshotRequestObject{
SnapshotId: "snap-123",
Body: &oapi.ForkSnapshotRequest{
Name: "forked-instance",
},
})
require.NoError(t, err)

created, ok := resp.(oapi.ForkSnapshot201JSONResponse)
require.True(t, ok, "expected 201 response")
assert.Equal(t, "forked-instance", created.Name)
assert.Equal(t, "snap-123", mockMgr.lastID)
require.NotNil(t, mockMgr.lastReq)
assert.Equal(t, "forked-instance", mockMgr.lastReq.Name)
}
17 changes: 0 additions & 17 deletions lib/forkvm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,6 @@ to work across implementations.
For networked forks, the fork gets a fresh host/guest identity (IP, MAC, TAP)
instead of reusing the source identity.

## Resume network handoff

Networked standby/running forks need a new host-side allocation, but the guest
memory snapshot still contains the source VM's old interface state. On restore,
Hypeman prepares the fork's TAP/IP/MAC before the VM resumes, then hands the new
guest network config to the guest through a small mailbox embedded in snapshot
memory. After resume, VMGenID tells the guest-agent that this is a restored VM;
the guest-agent reads the mailbox and applies the new MAC, address, route, and
neighbor state with netlink.

For API calls that return a running fork, Hypeman waits for a guest UDP
"applied" ack before returning, so the fast path still avoids making
host-initiated guest RPC/vsock contact as the first post-resume dependency. If
the mailbox path is unavailable, cannot start its ack listener, or does not ack
in time, restore falls back to the older host-initiated guest network
reconfigure path.

## Fork data copy behavior

- Guest directory copy is **sparse-only** for regular files.
Expand Down
2 changes: 0 additions & 2 deletions lib/instances/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,6 @@ func (m *manager) createInstance(
stored.Volumes = req.Volumes
}

ensureGuestInitiatedResumeNetworkMailbox(stored)

// 16. Create config disk (needs Instance for buildVMConfig)
inst := &Instance{StoredMetadata: *stored}
var proxyGuestConfig *egressproxy.GuestConfig
Expand Down
18 changes: 0 additions & 18 deletions lib/instances/fork_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,24 +319,6 @@ func TestApplyForkTargetStateStoppedRefreshesSnapshotForkCID(t *testing.T) {
assert.Equal(t, generateVsockCID(forkID), updated.StoredMetadata.VsockCID)
}

func TestForkReturnReadinessDoesNotUseMailboxEligibility(t *testing.T) {
t.Parallel()

stored := StoredMetadata{
HypervisorType: hypervisor.TypeFirecracker,
NetworkEnabled: true,
}
ensureGuestInitiatedResumeNetworkMailbox(&stored)
require.True(t, guestInitiatedResumeNetworkMailbox(&stored))

inst := &Instance{
StoredMetadata: stored,
State: StateInitializing,
}
assert.True(t, forkReturnNeedsGuestAgentReady(inst, false))
assert.False(t, forkReturnNeedsGuestAgentReady(inst, true))
}

func TestCloneStoredMetadataForFork_DeepCopiesReferenceFields(t *testing.T) {
t.Parallel()
startedAt := time.Now().Add(-2 * time.Minute)
Expand Down
235 changes: 0 additions & 235 deletions lib/instances/guest_resume_network.go

This file was deleted.

Loading
Loading