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
3 changes: 3 additions & 0 deletions cmd/vm/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ func launch(cmd *cobra.Command, dir string, r *record) error {
if err := requireCNIVNCPassword(r.Netns != "", r.VNCDisp, r.VNCPass); err != nil {
return err
}
if err := recoverNet(cmd, r); err != nil {
return fmt.Errorf("recover network: %w", err)
}
if isHostAMD() {
// macOS reads MSRs an AMD host lacks; without kvm.ignore_msrs KVM injects #GP (best-effort, host-global)
if err := os.WriteFile("/sys/module/kvm/parameters/ignore_msrs", []byte("1\n"), 0o600); err != nil {
Expand Down
36 changes: 34 additions & 2 deletions cmd/vm/net_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,19 @@ func newProvider(cmd *cobra.Command, r *record) (network.Network, error) {
return nil, fmt.Errorf("unknown --net mode %q (want user|tap|cni|bridge)", r.NetMode)
}

// netVMConfig pins CPU=1 so NetNumQueues yields a single-queue TAP matching QEMU's single-queue -netdev tap,ifname=
func netVMConfig(r *record) *types.VMConfig {
return &types.VMConfig{CPU: 1, Name: r.Name}
}

// provisionNet auto-creates a TAP via cocoon — the SAME forwarding plane as cocoon's CH/FC VMs.
func provisionNet(cmd *cobra.Command, r *record) (tap, netns, mac string, err error) {
provider, err := newProvider(cmd, r)
if err != nil {
return "", "", "", err
}
ctx := cliutil.CommandContext(cmd)
// CPU=1 => NetNumQueues yields a single-queue TAP matching QEMU's single-queue -netdev tap,ifname=
vmCfg := &types.VMConfig{CPU: 1, Name: r.Name}
vmCfg := netVMConfig(r)
nsPath, err := provider.Prepare(ctx, r.VMID, vmCfg)
if err != nil {
return "", "", "", fmt.Errorf("prepare network: %w", err)
Expand All @@ -92,6 +96,34 @@ func provisionNet(cmd *cobra.Command, r *record) (tap, netns, mac string, err er
return cfgs[0].TAP, nsPath, mac, nil
}

func recoverNet(cmd *cobra.Command, r *record) error {
if !r.TapOwned {
return nil
}
provider, err := newProvider(cmd, r)
if err != nil {
return err
}
ctx := cliutil.CommandContext(cmd)
expected := []*types.NetworkConfig{{TAP: r.Tap, MAC: r.MAC}}
verifyErr := provider.Verify(ctx, r.VMID, expected)
if verifyErr == nil {
return nil
}
log.WithFunc("cmd.vm.recoverNet").Warnf(ctx, "rebuild network for %s: %v", r.VMID, verifyErr)
vmCfg := netVMConfig(r)
nsPath, err := provider.Prepare(ctx, r.VMID, vmCfg)
if err != nil {
return err
}
cfgs, err := provider.Add(ctx, r.VMID, vmCfg, network.AddRecover(expected)...)
if err != nil {
return err
}
r.Tap, r.Netns = cfgs[0].TAP, nsPath
return nil
}

func teardownNet(ctx context.Context, cmd *cobra.Command, r *record) error {
if !r.TapOwned {
return nil
Expand Down
24 changes: 24 additions & 0 deletions cmd/vm/net_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,27 @@ func TestRMRetainsStateWhenNetworkTeardownFails(t *testing.T) {
t.Errorf("VM state was removed after teardown failure: %v", err)
}
}

func TestStartChecksOwnedNetworkBeforeQEMU(t *testing.T) {
stateDir := t.TempDir()
cmd := newLifecycleTestCommand(t, stateDir)
dir, err := home.VMDir(cmd, "macos-demo")
if err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatal(err)
}
r := &record{
Name: "macos-demo", VMID: "TESTVMID", Disk: filepath.Join(dir, "disk.qcow2"),
NetMode: "invalid", TapOwned: true,
}
if err := saveRec(dir, r); err != nil {
t.Fatal(err)
}

err = Start(cmd, []string{"macos-demo"})
if err == nil || !strings.Contains(err.Error(), "recover network: unknown --net mode") {
t.Fatalf("Start error = %v, want the network recovery failure before qemu launch", err)
}
}
2 changes: 2 additions & 0 deletions cmd/vm/net_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ func provisionNet(_ *cobra.Command, _ *record) (tap, netns, mac string, err erro
return "", "", "", fmt.Errorf("auto-create TAP / CNI / bridge networking requires Linux (running on %s); pass a pre-created --tap or use --net user", runtime.GOOS)
}

func recoverNet(_ *cobra.Command, _ *record) error { return nil }

func teardownNet(_ context.Context, _ *cobra.Command, _ *record) error { return nil }

func toggleNet(_ *cobra.Command, _ *record, _ bool) {}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/cocoonstack/cocoon-macos
go 1.27.0

require (
github.com/cocoonstack/cocoon v0.6.9-0.20260923034234-4a3ed8c35796
github.com/cocoonstack/cocoon v0.6.9-0.20260923161722-22b7392e16b0
github.com/gofrs/flock v0.13.0
github.com/opencontainers/image-spec v1.1.1
github.com/projecteru2/core v0.1.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILM
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo=
github.com/cockroachdb/redact v1.1.8 h1:8eVLLj6juKxiKrAEw2b8cJvNqWq++U8WOfQFuL7KTaA=
github.com/cockroachdb/redact v1.1.8/go.mod h1:GceHHpJ0rMDpYARL5In88Alq/xMBUtVlz7Qxix6ZVkw=
github.com/cocoonstack/cocoon v0.6.9-0.20260923034234-4a3ed8c35796 h1:P3b28cTPRfbwKgCBj5swqJhwXYdnEtIC8xrKS/PAARM=
github.com/cocoonstack/cocoon v0.6.9-0.20260923034234-4a3ed8c35796/go.mod h1:B02b6pewrk1DbrRn+iBtDc8LNQWxwot2qF1j4YX5/jg=
github.com/cocoonstack/cocoon v0.6.9-0.20260923161722-22b7392e16b0 h1:RJe3v2i/sP1BymsYlJx8+0DqnTRxExvYyi1rslV7tvg=
github.com/cocoonstack/cocoon v0.6.9-0.20260923161722-22b7392e16b0/go.mod h1:B02b6pewrk1DbrRn+iBtDc8LNQWxwot2qF1j4YX5/jg=
github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo=
github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4=
github.com/containernetworking/plugins v1.9.1 h1:8oU6WsIsU3bpnNZuvHp74a6cE1MJwbj2P7s4/yTUNlA=
Expand Down
Loading