spiegel_podman/pkg/machine/hyperv/stubber_test.go
Mario Loriedo 92d186f20d Hyperv machine init/rm: skip win registry operations when possible
Hyper-V VMs require some specific Windows registry keys to allow the
communication between the host and the guest. Creating these registry
keys require elevated privileges.

These keys were created during the first Podman `machine init` and
removed when the last Podman machine is removed.

In this commit we skip the creation of the keys if they already exist in
the registry. So that admin privileges aren't required anymore, even for
the creation of the first Podman machine.

In other words, if the keys are pre-created by an administrator, user
will be able to create and remove machines without requiring to run any
elevated command. Even for the first podman machine.

Signed-off-by: Mario Loriedo <mario.loriedo@gmail.com>
2026-04-29 23:00:29 +02:00

180 lines
4.8 KiB
Go

//go:build windows
package hyperv
import (
"testing"
"github.com/stretchr/testify/assert"
"go.podman.io/podman/v6/pkg/machine/hyperv/vsock"
"go.podman.io/podman/v6/pkg/machine/vmconfigs"
)
func TestCanCreate(t *testing.T) {
t.Parallel()
tests := []struct {
name string
isElevated bool
vsockEntriesExist bool
isHyperVAdmin bool
mounts int
expectedErr error
}{
{
name: "in elevated process can always create",
isElevated: true,
vsockEntriesExist: false,
isHyperVAdmin: false,
mounts: 2,
expectedErr: nil,
},
{
name: "not elevated, no vsock entries",
isElevated: false,
vsockEntriesExist: false,
isHyperVAdmin: true,
expectedErr: ErrHypervRegistryInitRequiresElevation,
},
{
name: "not elevated, vsock entries exist, not hyperv admin",
isElevated: false,
vsockEntriesExist: true,
isHyperVAdmin: false,
expectedErr: ErrHypervUserNotInAdminGroup,
},
{
name: "not elevated, vsock entries exist, hyperv admin",
isElevated: false,
vsockEntriesExist: true,
isHyperVAdmin: true,
expectedErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
checks := permissionChecks{
isElevatedProcess: func() bool { return tt.isElevated },
isHyperVAdminMember: func() bool { return tt.isHyperVAdmin },
vsockEntriesExist: func(int) bool { return tt.vsockEntriesExist },
}
err := checkCanCreate(checks, tt.mounts)
assert.ErrorIs(t, err, tt.expectedErr)
})
}
}
func TestCanRemove(t *testing.T) {
t.Parallel()
tests := []struct {
name string
isElevatedProcess bool
isHyperVAdminMember bool
isLegacyMachine bool
skipVsockEntriesRemoval bool
isLastMachine bool
expectedErr error
}{
{
name: "elevated process can always remove",
isElevatedProcess: true,
isHyperVAdminMember: false,
isLegacyMachine: false,
skipVsockEntriesRemoval: false,
isLastMachine: true,
expectedErr: nil,
},
{
name: "not elevated, legacy machine requires elevation",
isElevatedProcess: false,
isHyperVAdminMember: false,
isLegacyMachine: true,
skipVsockEntriesRemoval: false,
isLastMachine: true,
expectedErr: ErrHypervLegacyMachineRequiresElevation,
},
{
name: "not elevated, not hyperv admins member",
isElevatedProcess: false,
isHyperVAdminMember: false,
isLegacyMachine: false,
skipVsockEntriesRemoval: false,
isLastMachine: true,
expectedErr: ErrHypervUserNotInAdminGroup,
},
{
name: "not elevated, hyperv admins member, not last machine",
isElevatedProcess: false,
isHyperVAdminMember: true,
isLegacyMachine: false,
skipVsockEntriesRemoval: false,
isLastMachine: false,
expectedErr: nil,
},
{
name: "not elevated, hyperv admins member, last machine, skip vsock entries removal",
isElevatedProcess: false,
isHyperVAdminMember: true,
isLegacyMachine: false,
skipVsockEntriesRemoval: true,
isLastMachine: true,
expectedErr: nil,
},
{
name: "not elevated, hyperv admins member, last machine, don't skip vsock entries removal",
isElevatedProcess: false,
isHyperVAdminMember: true,
isLegacyMachine: false,
skipVsockEntriesRemoval: false,
isLastMachine: true,
expectedErr: ErrHypervRegistryRemoveRequiresElevation,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
checks := permissionChecks{
isElevatedProcess: func() bool { return tt.isElevatedProcess },
isHyperVAdminMember: func() bool { return tt.isHyperVAdminMember },
existingMachinesNum: func() (int, error) {
if tt.isLastMachine {
return 1, nil
} else {
return 2, nil
}
},
}
vsockMachineName := ""
if tt.isLegacyMachine {
vsockMachineName = "podmanmachine"
}
mc := &vmconfigs.MachineConfig{
HyperVHypervisor: &vmconfigs.HyperVConfig{
ReadyVsock: vsock.HVSockRegistryEntry{
MachineName: vsockMachineName,
KeepAfterMachineRemove: tt.skipVsockEntriesRemoval,
},
NetworkVSock: vsock.HVSockRegistryEntry{
KeepAfterMachineRemove: tt.skipVsockEntriesRemoval,
},
},
}
err := checkCanRemove(mc, checks)
if tt.expectedErr != nil {
assert.EqualError(t, err, tt.expectedErr.Error())
} else {
assert.NoError(t, err)
}
})
}
}