1
0
mirror of https://github.com/containers/podman.git synced 2026-02-05 06:45:31 +01:00

fix hyperv ignition cleanup and error handling

this fixes two bugs in Hyper-V VM ignition handling:

1. Changed `err := readAndSplitIgnition()` to
`err = readAndSplitIgnition()` so the deferred cleanup function can
properly detect errors and clean up ignition
2. The function was trying to remove keys with pattern `vm.ElementName + index`, but
the actual keys were created using `"ignition.config." + index` pattern.

It also improve the deletion by calculating in how many parts the ignition file is splitted when being added in the registry and delete just them instead of blidnly iterating through 0-50.

Signed-off-by: lstocchi <lstocchi@redhat.com>
This commit is contained in:
lstocchi
2026-01-21 15:06:08 +01:00
parent 95230cb315
commit cfa1b51775

View File

@@ -13,6 +13,7 @@ import (
"github.com/Microsoft/go-winio"
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
"github.com/containers/libhvee/pkg/hypervctl"
"github.com/containers/libhvee/pkg/kvp/ginsu"
"github.com/containers/podman/v6/pkg/machine"
"github.com/containers/podman/v6/pkg/machine/define"
"github.com/containers/podman/v6/pkg/machine/env"
@@ -190,7 +191,7 @@ func (h HyperVStubber) Remove(mc *vmconfigs.MachineConfig) ([]string, func() err
// Remove ignition registry entries - not a fatal error
// for vm removal
// TODO we could improve this by recommending an action be done
if err := removeIgnitionFromRegistry(vm); err != nil {
if err := removeIgnitionFromRegistry(mc, vm); err != nil {
logrus.Errorf("unable to remove ignition registry entries: %q", err)
}
@@ -374,27 +375,20 @@ func (h HyperVStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func(
go callbackFuncs.CleanOnSignal()
if mc.IsFirstBoot() {
// Add ignition entries to windows registry
// for first boot only
if err := readAndSplitIgnition(mc, vm); err != nil {
return nil, nil, err
}
// this is added because if the machine does not start
// properly on first boot, the next boot will be considered
// the first boot again and the addition of the ignition
// entries might fail?
//
// the downside is that if the start fails and then a rm
// is run, it will puke error messages about the ignition.
//
// TODO detect if ignition was run from a failed boot earlier
// and skip. Maybe this could be done with checking a k/v
// pair
// entries will fail because the key/value pairs already exist.
rmIgnCallbackFunc := func() error {
return removeIgnitionFromRegistry(vm)
return removeIgnitionFromRegistry(mc, vm)
}
callbackFuncs.Add(rmIgnCallbackFunc)
// Add ignition entries to windows registry
// for first boot only
if err = readAndSplitIgnition(mc, vm); err != nil {
return nil, nil, err
}
}
waitReady, listener, err := mc.HyperVHypervisor.ReadyVsock.ListenSetupWait()
@@ -660,30 +654,41 @@ func resizeDisk(newSize strongunits.GiB, imagePath *define.VMFile) error {
return nil
}
// readAndSplitIgnition reads the ignition file and splits it into key:value pairs
func readAndSplitIgnition(mc *vmconfigs.MachineConfig, vm *hypervctl.VirtualMachine) error {
func getIgnitionReader(mc *vmconfigs.MachineConfig) (*bytes.Reader, error) {
ignFile, err := mc.IgnitionFile()
if err != nil {
return err
return nil, err
}
ign, err := ignFile.Read()
if err != nil {
return nil, err
}
return bytes.NewReader(ign), nil
}
// readAndSplitIgnition reads the ignition file and splits it into key:value pairs
func readAndSplitIgnition(mc *vmconfigs.MachineConfig, vm *hypervctl.VirtualMachine) error {
reader, err := getIgnitionReader(mc)
if err != nil {
return err
}
reader := bytes.NewReader(ign)
return vm.SplitAndAddIgnition("ignition.config.", reader)
}
func removeIgnitionFromRegistry(vm *hypervctl.VirtualMachine) error {
func removeIgnitionFromRegistry(mc *vmconfigs.MachineConfig, vm *hypervctl.VirtualMachine) error {
// because the vm is down at this point, we cannot query hyperv for these key value pairs.
// therefore we blindly iterate from 0-50 and delete the key/value pairs. hyperv does not
// raise an error if the key is not present
//
for i := 0; i < 50; i++ {
// this is a well known "key" defined in libhvee and is the vm name
// plus an index starting at 0
key := fmt.Sprintf("%s%d", vm.ElementName, i)
// therefore we recalculate the number of parts ignition file consists of and
// remove all ignition key/value pairs the same way they were added
reader, err := getIgnitionReader(mc)
if err != nil {
return err
}
parts, err := ginsu.Dice(reader)
if err != nil {
return err
}
for idx := range parts {
key := fmt.Sprintf("ignition.config.%d", idx)
if err := vm.RemoveKeyValuePairNoWait(key); err != nil {
return err
}