podman machine init: do not write config unlocked

First make sure we check that a given VM exist when holding the VM lock
for it. The check in cmd/podman/machine/init.go is a nice quick out but
not enough to ensure that 2 processes to not create the same VM at the
same time. The only way to ensure this is by holding the lock and
checking if the VM config file exists.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger 2024-03-07 14:58:25 +01:00
parent 3c9c5be7da
commit 7a75914921
No known key found for this signature in database
GPG Key ID: EB145DD938A3CAF2
3 changed files with 34 additions and 30 deletions

View File

@ -208,16 +208,11 @@ func initMachine(cmd *cobra.Command, args []string) error {
// return err
// }
mc, err := shim.Init(initOpts, provider)
err = shim.Init(initOpts, provider)
if err != nil {
return err
}
// TODO callback needed for the configuration file
if err := mc.Write(); err != nil {
return err
}
newMachineEvent(events.Init, events.Event{Name: initOpts.Name})
fmt.Println("Machine init complete")

View File

@ -15,6 +15,7 @@ import (
machineDefine "github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/env"
"github.com/containers/podman/v5/pkg/machine/ignition"
"github.com/containers/podman/v5/pkg/machine/lock"
"github.com/containers/podman/v5/pkg/machine/proxyenv"
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
"github.com/containers/podman/v5/utils"
@ -66,7 +67,7 @@ func List(vmstubbers []vmconfigs.VMProvider, _ machine.ListOptions) ([]*machine.
return lrs, nil
}
func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.MachineConfig, error) {
func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) error {
var (
err error
imageExtension string
@ -79,21 +80,28 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
dirs, err := env.GetMachineDirs(mp.VMType())
if err != nil {
return nil, err
return err
}
sshIdentityPath, err := env.GetSSHIdentityPath(machineDefine.DefaultIdentityName)
if err != nil {
return nil, err
return err
}
sshKey, err := machine.GetSSHKeys(sshIdentityPath)
if err != nil {
return nil, err
return err
}
mc, err := vmconfigs.NewMachineConfig(opts, dirs, sshIdentityPath, mp.VMType())
machineLock, err := lock.GetMachineLock(opts.Name, dirs.ConfigDir.GetPath())
if err != nil {
return nil, err
return err
}
machineLock.Lock()
defer machineLock.Unlock()
mc, err := vmconfigs.NewMachineConfig(opts, dirs, sshIdentityPath, mp.VMType(), machineLock)
if err != nil {
return err
}
mc.Version = vmconfigs.MachineConfigVersion
@ -128,7 +136,7 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
imagePath, err = dirs.DataDir.AppendToNewVMFile(fmt.Sprintf("%s-%s%s", opts.Name, runtime.GOARCH, imageExtension), nil)
if err != nil {
return nil, err
return err
}
mc.ImagePath = imagePath
@ -142,7 +150,7 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
// "docker://quay.io/something/someManifest
if err := mp.GetDisk(opts.Image, dirs, mc); err != nil {
return nil, err
return err
}
callbackFuncs.Add(mc.ImagePath.Delete)
@ -151,7 +159,7 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
ignitionFile, err := mc.IgnitionFile()
if err != nil {
return nil, err
return err
}
uid := os.Getuid()
@ -184,22 +192,22 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
// copy it into the conf dir
if len(opts.IgnitionPath) > 0 {
err = ignBuilder.BuildWithIgnitionFile(opts.IgnitionPath)
return nil, err
return err
}
err = ignBuilder.GenerateIgnitionConfig()
if err != nil {
return nil, err
return err
}
readyIgnOpts, err := mp.PrepareIgnition(mc, &ignBuilder)
if err != nil {
return nil, err
return err
}
readyUnitFile, err := ignition.CreateReadyUnitFile(mp.VMType(), readyIgnOpts)
if err != nil {
return nil, err
return err
}
readyUnit := ignition.Unit{
@ -216,7 +224,7 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
// TODO AddSSHConnectionToPodmanSocket could take an machineconfig instead
if err := connection.AddSSHConnectionsToPodmanSocket(mc.HostUser.UID, mc.SSH.Port, mc.SSH.IdentityPath, mc.Name, mc.SSH.RemoteUsername, opts); err != nil {
return nil, err
return err
}
cleanup := func() error {
@ -226,15 +234,15 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
err = mp.CreateVM(createOpts, mc, &ignBuilder)
if err != nil {
return nil, err
return err
}
err = ignBuilder.Build()
if err != nil {
return nil, err
return err
}
return mc, err
return mc.Write()
}
// VMExists looks across given providers for a machine's existence. returns the actual config and found bool

View File

@ -18,6 +18,7 @@ import (
"github.com/containers/podman/v5/pkg/machine/lock"
"github.com/containers/podman/v5/pkg/machine/ports"
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/lockfile"
"github.com/sirupsen/logrus"
)
@ -42,16 +43,11 @@ var (
type RemoteConnectionType string
// NewMachineConfig creates the initial machine configuration file from cli options
func NewMachineConfig(opts define.InitOptions, dirs *define.MachineDirs, sshIdentityPath string, vmtype define.VMType) (*MachineConfig, error) {
// NewMachineConfig creates the initial machine configuration file from cli options.
func NewMachineConfig(opts define.InitOptions, dirs *define.MachineDirs, sshIdentityPath string, vmtype define.VMType, machineLock *lockfile.LockFile) (*MachineConfig, error) {
mc := new(MachineConfig)
mc.Name = opts.Name
mc.dirs = dirs
machineLock, err := lock.GetMachineLock(opts.Name, dirs.ConfigDir.GetPath())
if err != nil {
return nil, err
}
mc.lock = machineLock
// Assign Dirs
@ -60,6 +56,11 @@ func NewMachineConfig(opts define.InitOptions, dirs *define.MachineDirs, sshIden
return nil, err
}
mc.configPath = cf
// Given that we are locked now and check again that the config file does not exists,
// if it does it means the VM was already created and we should error.
if _, err := os.Stat(cf.Path); err == nil {
return nil, fmt.Errorf("%s: %w", opts.Name, define.ErrVMAlreadyExists)
}
if vmtype != define.QemuVirt && len(opts.USBs) > 0 {
return nil, fmt.Errorf("USB host passthrough not supported for %s machines", vmtype)