mirror of
https://github.com/containers/podman.git
synced 2025-06-22 09:58:10 +08:00
Merge pull request #20626 from jakecorrenti/ignition-builder
Refactor Ignition configuration for virt providers
This commit is contained in:
@ -129,11 +129,9 @@ func (v AppleHVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ignitionPath, err := define.NewMachineFile(filepath.Join(configDir, m.Name)+".ign", nil)
|
if err := machine.SetIgnitionFile(&m.IgnitionFile, vmtype, m.Name); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.IgnitionFile = *ignitionPath
|
|
||||||
|
|
||||||
// Set creation time
|
// Set creation time
|
||||||
m.Created = time.Now()
|
m.Created = time.Now()
|
||||||
|
@ -50,6 +50,22 @@ type VfkitHelper struct {
|
|||||||
VirtualMachine *vfConfig.VirtualMachine
|
VirtualMachine *vfConfig.VirtualMachine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appleHVReadyUnit is a unit file that sets up the virtual serial device
|
||||||
|
// where when the VM is done configuring, it will send an ack
|
||||||
|
// so a listening host knows it can begin interacting with it
|
||||||
|
const appleHVReadyUnit = `[Unit]
|
||||||
|
Requires=dev-virtio\\x2dports-%s.device
|
||||||
|
After=remove-moby.service sshd.socket sshd.service
|
||||||
|
OnFailure=emergency.target
|
||||||
|
OnFailureJobMode=isolate
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/bin/sh -c '/usr/bin/echo Ready | socat - VSOCK-CONNECT:2:1025'
|
||||||
|
[Install]
|
||||||
|
RequiredBy=default.target
|
||||||
|
`
|
||||||
|
|
||||||
type MacMachine struct {
|
type MacMachine struct {
|
||||||
// ConfigPath is the fully qualified path to the configuration file
|
// ConfigPath is the fully qualified path to the configuration file
|
||||||
ConfigPath define.VMFile
|
ConfigPath define.VMFile
|
||||||
@ -136,51 +152,6 @@ func (m *MacMachine) addMountsToVM(opts machine.InitOptions, virtiofsMnts *[]mac
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeIgnitionConfigFile generates the ignition config and writes it to the filesystem
|
|
||||||
func (m *MacMachine) writeIgnitionConfigFile(opts machine.InitOptions, key string, virtiofsMnts *[]machine.VirtIoFs) error {
|
|
||||||
// Write the ignition file
|
|
||||||
ign := machine.DynamicIgnition{
|
|
||||||
Name: opts.Username,
|
|
||||||
Key: key,
|
|
||||||
VMName: m.Name,
|
|
||||||
VMType: machine.AppleHvVirt,
|
|
||||||
TimeZone: opts.TimeZone,
|
|
||||||
WritePath: m.IgnitionFile.GetPath(),
|
|
||||||
UID: m.UID,
|
|
||||||
Rootful: m.Rootful,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ign.GenerateIgnitionConfig(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ready is a unit file that sets up the virtual serial device
|
|
||||||
// where when the VM is done configuring, it will send an ack
|
|
||||||
// so a listening host knows it can being interacting with it
|
|
||||||
ready := `[Unit]
|
|
||||||
Requires=dev-virtio\\x2dports-%s.device
|
|
||||||
After=remove-moby.service sshd.socket sshd.service
|
|
||||||
OnFailure=emergency.target
|
|
||||||
OnFailureJobMode=isolate
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
RemainAfterExit=yes
|
|
||||||
ExecStart=/bin/sh -c '/usr/bin/echo Ready | socat - VSOCK-CONNECT:2:1025'
|
|
||||||
[Install]
|
|
||||||
RequiredBy=default.target
|
|
||||||
`
|
|
||||||
readyUnit := machine.Unit{
|
|
||||||
Enabled: machine.BoolToPtr(true),
|
|
||||||
Name: "ready.service",
|
|
||||||
Contents: machine.StrToPtr(fmt.Sprintf(ready, "vsock")),
|
|
||||||
}
|
|
||||||
virtiofsUnits := generateSystemDFilesForVirtiofsMounts(*virtiofsMnts)
|
|
||||||
ign.Cfg.Systemd.Units = append(ign.Cfg.Systemd.Units, readyUnit)
|
|
||||||
ign.Cfg.Systemd.Units = append(ign.Cfg.Systemd.Units, virtiofsUnits...)
|
|
||||||
|
|
||||||
return ign.Write()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||||
var (
|
var (
|
||||||
key string
|
key string
|
||||||
@ -280,6 +251,17 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder := machine.NewIgnitionBuilder(machine.DynamicIgnition{
|
||||||
|
Name: opts.Username,
|
||||||
|
Key: key,
|
||||||
|
VMName: m.Name,
|
||||||
|
VMType: machine.AppleHvVirt,
|
||||||
|
TimeZone: opts.TimeZone,
|
||||||
|
WritePath: m.IgnitionFile.GetPath(),
|
||||||
|
UID: m.UID,
|
||||||
|
Rootful: m.Rootful,
|
||||||
|
})
|
||||||
|
|
||||||
if len(opts.IgnitionPath) < 1 {
|
if len(opts.IgnitionPath) < 1 {
|
||||||
key, err = machine.CreateSSHKeys(m.IdentityPath)
|
key, err = machine.CreateSSHKeys(m.IdentityPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -289,14 +271,22 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.IgnitionPath) > 0 {
|
if len(opts.IgnitionPath) > 0 {
|
||||||
inputIgnition, err := os.ReadFile(opts.IgnitionPath)
|
return false, builder.BuildWithIgnitionFile(opts.IgnitionPath)
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return false, os.WriteFile(m.IgnitionFile.GetPath(), inputIgnition, 0644)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := builder.GenerateIgnitionConfig(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WithUnit(machine.Unit{
|
||||||
|
Enabled: machine.BoolToPtr(true),
|
||||||
|
Name: "ready.service",
|
||||||
|
Contents: machine.StrToPtr(fmt.Sprintf(appleHVReadyUnit, "vsock")),
|
||||||
|
})
|
||||||
|
builder.WithUnit(generateSystemDFilesForVirtiofsMounts(virtiofsMnts)...)
|
||||||
|
|
||||||
// TODO Ignition stuff goes here
|
// TODO Ignition stuff goes here
|
||||||
err = m.writeIgnitionConfigFile(opts, key, &virtiofsMnts)
|
err = builder.Build()
|
||||||
callbackFuncs.Add(m.IgnitionFile.Delete)
|
callbackFuncs.Add(m.IgnitionFile.Delete)
|
||||||
|
|
||||||
return err == nil, err
|
return err == nil, err
|
||||||
|
@ -126,11 +126,9 @@ func (v HyperVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM,
|
|||||||
|
|
||||||
m.ConfigPath = *configPath
|
m.ConfigPath = *configPath
|
||||||
|
|
||||||
ignitionPath, err := define.NewMachineFile(filepath.Join(configDir, m.Name)+".ign", nil)
|
if err := machine.SetIgnitionFile(&m.IgnitionFile, vmtype, m.Name); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.IgnitionFile = *ignitionPath
|
|
||||||
|
|
||||||
// Set creation time
|
// Set creation time
|
||||||
m.Created = time.Now()
|
m.Created = time.Now()
|
||||||
|
@ -43,6 +43,58 @@ const (
|
|||||||
apiUpTimeout = 20 * time.Second
|
apiUpTimeout = 20 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// hyperVReadyUnit is a unit file that sets up the virtual serial device
|
||||||
|
// where when the VM is done configuring, it will send an ack
|
||||||
|
// so a listening host knows it can begin interacting with it
|
||||||
|
//
|
||||||
|
// VSOCK-CONNECT:2 <- shortcut to connect to the hostvm
|
||||||
|
const hyperVReadyUnit = `[Unit]
|
||||||
|
After=remove-moby.service sshd.socket sshd.service
|
||||||
|
After=systemd-user-sessions.service
|
||||||
|
OnFailure=emergency.target
|
||||||
|
OnFailureJobMode=isolate
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/bin/sh -c '/usr/bin/echo Ready | socat - VSOCK-CONNECT:2:%d'
|
||||||
|
[Install]
|
||||||
|
RequiredBy=default.target
|
||||||
|
`
|
||||||
|
|
||||||
|
// hyperVVsockNetUnit is a systemd unit file that calls the vm helper utility
|
||||||
|
// needed to take traffic from a network vsock0 device to the actual vsock
|
||||||
|
// and onto the host
|
||||||
|
const hyperVVsockNetUnit = `
|
||||||
|
[Unit]
|
||||||
|
Description=vsock_network
|
||||||
|
After=NetworkManager.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/libexec/podman/gvforwarder -preexisting -iface vsock0 -url vsock://2:%d/connect
|
||||||
|
ExecStartPost=/usr/bin/nmcli c up vsock0
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
`
|
||||||
|
|
||||||
|
const hyperVVsockNMConnection = `
|
||||||
|
[connection]
|
||||||
|
id=vsock0
|
||||||
|
type=tun
|
||||||
|
interface-name=vsock0
|
||||||
|
|
||||||
|
[tun]
|
||||||
|
mode=2
|
||||||
|
|
||||||
|
[802-3-ethernet]
|
||||||
|
cloned-mac-address=5A:94:EF:E4:0C:EE
|
||||||
|
|
||||||
|
[ipv4]
|
||||||
|
method=auto
|
||||||
|
|
||||||
|
[proxy]
|
||||||
|
`
|
||||||
|
|
||||||
type HyperVMachine struct {
|
type HyperVMachine struct {
|
||||||
// ConfigPath is the fully qualified path to the configuration file
|
// ConfigPath is the fully qualified path to the configuration file
|
||||||
ConfigPath define.VMFile
|
ConfigPath define.VMFile
|
||||||
@ -93,104 +145,6 @@ func (m *HyperVMachine) addNetworkAndReadySocketsToRegistry() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeIgnitionConfigFile generates the ignition config and writes it to the
|
|
||||||
// filesystem
|
|
||||||
func (m *HyperVMachine) writeIgnitionConfigFile(opts machine.InitOptions, user, key string) error {
|
|
||||||
ign := machine.DynamicIgnition{
|
|
||||||
Name: user,
|
|
||||||
Key: key,
|
|
||||||
VMName: m.Name,
|
|
||||||
VMType: machine.HyperVVirt,
|
|
||||||
TimeZone: opts.TimeZone,
|
|
||||||
WritePath: m.IgnitionFile.GetPath(),
|
|
||||||
UID: m.UID,
|
|
||||||
Rootful: m.Rootful,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ign.GenerateIgnitionConfig(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ready is a unit file that sets up the virtual serial device
|
|
||||||
// where when the VM is done configuring, it will send an ack
|
|
||||||
// so a listening host knows it can being interacting with it
|
|
||||||
//
|
|
||||||
// VSOCK-CONNECT:2 <- shortcut to connect to the hostvm
|
|
||||||
ready := `[Unit]
|
|
||||||
After=remove-moby.service sshd.socket sshd.service
|
|
||||||
After=systemd-user-sessions.service
|
|
||||||
OnFailure=emergency.target
|
|
||||||
OnFailureJobMode=isolate
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
RemainAfterExit=yes
|
|
||||||
ExecStart=/bin/sh -c '/usr/bin/echo Ready | socat - VSOCK-CONNECT:2:%d'
|
|
||||||
[Install]
|
|
||||||
RequiredBy=default.target
|
|
||||||
`
|
|
||||||
readyUnit := machine.Unit{
|
|
||||||
Enabled: machine.BoolToPtr(true),
|
|
||||||
Name: "ready.service",
|
|
||||||
Contents: machine.StrToPtr(fmt.Sprintf(ready, m.ReadyHVSock.Port)),
|
|
||||||
}
|
|
||||||
|
|
||||||
// userNetwork is a systemd unit file that calls the vm helpoer utility
|
|
||||||
// needed to take traffic from a network vsock0 device to the actual vsock
|
|
||||||
// and onto the host
|
|
||||||
userNetwork := `
|
|
||||||
[Unit]
|
|
||||||
Description=vsock_network
|
|
||||||
After=NetworkManager.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
ExecStart=/usr/libexec/podman/gvforwarder -preexisting -iface vsock0 -url vsock://2:%d/connect
|
|
||||||
ExecStartPost=/usr/bin/nmcli c up vsock0
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
`
|
|
||||||
vsockNetUnit := machine.Unit{
|
|
||||||
Contents: machine.StrToPtr(fmt.Sprintf(userNetwork, m.NetworkHVSock.Port)),
|
|
||||||
Enabled: machine.BoolToPtr(true),
|
|
||||||
Name: "vsock-network.service",
|
|
||||||
}
|
|
||||||
|
|
||||||
ign.Cfg.Systemd.Units = append(ign.Cfg.Systemd.Units, readyUnit, vsockNetUnit)
|
|
||||||
|
|
||||||
vSockNMConnection := `
|
|
||||||
[connection]
|
|
||||||
id=vsock0
|
|
||||||
type=tun
|
|
||||||
interface-name=vsock0
|
|
||||||
|
|
||||||
[tun]
|
|
||||||
mode=2
|
|
||||||
|
|
||||||
[802-3-ethernet]
|
|
||||||
cloned-mac-address=5A:94:EF:E4:0C:EE
|
|
||||||
|
|
||||||
[ipv4]
|
|
||||||
method=auto
|
|
||||||
|
|
||||||
[proxy]
|
|
||||||
`
|
|
||||||
|
|
||||||
ign.Cfg.Storage.Files = append(ign.Cfg.Storage.Files, machine.File{
|
|
||||||
Node: machine.Node{
|
|
||||||
Path: "/etc/NetworkManager/system-connections/vsock0.nmconnection",
|
|
||||||
},
|
|
||||||
FileEmbedded1: machine.FileEmbedded1{
|
|
||||||
Append: nil,
|
|
||||||
Contents: machine.Resource{
|
|
||||||
Source: machine.EncodeDataURLPtr(vSockNMConnection),
|
|
||||||
},
|
|
||||||
Mode: machine.IntToPtr(0600),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return ign.Write()
|
|
||||||
}
|
|
||||||
|
|
||||||
// readAndSplitIgnition reads the ignition file and splits it into key:value pairs
|
// readAndSplitIgnition reads the ignition file and splits it into key:value pairs
|
||||||
func (m *HyperVMachine) readAndSplitIgnition() error {
|
func (m *HyperVMachine) readAndSplitIgnition() error {
|
||||||
ignFile, err := m.IgnitionFile.Read()
|
ignFile, err := m.IgnitionFile.Read()
|
||||||
@ -295,14 +249,21 @@ func (m *HyperVMachine) Init(opts machine.InitOptions) (bool, error) {
|
|||||||
}
|
}
|
||||||
m.Rootful = opts.Rootful
|
m.Rootful = opts.Rootful
|
||||||
|
|
||||||
|
builder := machine.NewIgnitionBuilder(machine.DynamicIgnition{
|
||||||
|
Name: m.RemoteUsername,
|
||||||
|
Key: key,
|
||||||
|
VMName: m.Name,
|
||||||
|
VMType: machine.HyperVVirt,
|
||||||
|
TimeZone: opts.TimeZone,
|
||||||
|
WritePath: m.IgnitionFile.GetPath(),
|
||||||
|
UID: m.UID,
|
||||||
|
Rootful: m.Rootful,
|
||||||
|
})
|
||||||
|
|
||||||
// If the user provides an ignition file, we need to
|
// If the user provides an ignition file, we need to
|
||||||
// copy it into the conf dir
|
// copy it into the conf dir
|
||||||
if len(opts.IgnitionPath) > 0 {
|
if len(opts.IgnitionPath) > 0 {
|
||||||
inputIgnition, err := os.ReadFile(opts.IgnitionPath)
|
return false, builder.BuildWithIgnitionFile(opts.IgnitionPath)
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return false, os.WriteFile(m.IgnitionFile.GetPath(), inputIgnition, 0644)
|
|
||||||
}
|
}
|
||||||
callbackFuncs.Add(m.IgnitionFile.Delete)
|
callbackFuncs.Add(m.IgnitionFile.Delete)
|
||||||
|
|
||||||
@ -310,8 +271,36 @@ func (m *HyperVMachine) Init(opts machine.InitOptions) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the ignition file
|
if err := builder.GenerateIgnitionConfig(); err != nil {
|
||||||
if err := m.writeIgnitionConfigFile(opts, m.RemoteUsername, key); err != nil {
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WithUnit(machine.Unit{
|
||||||
|
Enabled: machine.BoolToPtr(true),
|
||||||
|
Name: "ready.service",
|
||||||
|
Contents: machine.StrToPtr(fmt.Sprintf(hyperVReadyUnit, m.ReadyHVSock.Port)),
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.WithUnit(machine.Unit{
|
||||||
|
Contents: machine.StrToPtr(fmt.Sprintf(hyperVVsockNetUnit, m.NetworkHVSock.Port)),
|
||||||
|
Enabled: machine.BoolToPtr(true),
|
||||||
|
Name: "vsock-network.service",
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.WithFile(machine.File{
|
||||||
|
Node: machine.Node{
|
||||||
|
Path: "/etc/NetworkManager/system-connections/vsock0.nmconnection",
|
||||||
|
},
|
||||||
|
FileEmbedded1: machine.FileEmbedded1{
|
||||||
|
Append: nil,
|
||||||
|
Contents: machine.Resource{
|
||||||
|
Source: machine.EncodeDataURLPtr(hyperVVsockNMConnection),
|
||||||
|
},
|
||||||
|
Mode: machine.IntToPtr(0600),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := builder.Build(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/common/libnetwork/etchosts"
|
"github.com/containers/common/libnetwork/etchosts"
|
||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
|
"github.com/containers/podman/v4/pkg/machine/define"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -694,3 +695,65 @@ func GetPodmanDockerTmpConfig(uid int, rootful bool, newline bool) string {
|
|||||||
|
|
||||||
return fmt.Sprintf("L+ /run/docker.sock - - - - %s%s", podmanSock, suffix)
|
return fmt.Sprintf("L+ /run/docker.sock - - - - %s%s", podmanSock, suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetIgnitionFile creates a new Machine File for the machine's ignition file
|
||||||
|
// and assignes the handle to `loc`
|
||||||
|
func SetIgnitionFile(loc *define.VMFile, vmtype VMType, vmName string) error {
|
||||||
|
vmConfigDir, err := GetConfDir(vmtype)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ignitionFile, err := define.NewMachineFile(filepath.Join(vmConfigDir, vmName+".ign"), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*loc = *ignitionFile
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type IgnitionBuilder struct {
|
||||||
|
dynamicIgnition DynamicIgnition
|
||||||
|
units []Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIgnitionBuilder generates a new IgnitionBuilder type using the
|
||||||
|
// base `DynamicIgnition` object
|
||||||
|
func NewIgnitionBuilder(dynamicIgnition DynamicIgnition) IgnitionBuilder {
|
||||||
|
return IgnitionBuilder{
|
||||||
|
dynamicIgnition,
|
||||||
|
[]Unit{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateIgnitionConfig generates the ignition config
|
||||||
|
func (i *IgnitionBuilder) GenerateIgnitionConfig() error {
|
||||||
|
return i.dynamicIgnition.GenerateIgnitionConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithUnit adds systemd units to the internal `DynamicIgnition` config
|
||||||
|
func (i *IgnitionBuilder) WithUnit(units ...Unit) {
|
||||||
|
i.dynamicIgnition.Cfg.Systemd.Units = append(i.dynamicIgnition.Cfg.Systemd.Units, units...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFile adds storage files to the internal `DynamicIgnition` config
|
||||||
|
func (i *IgnitionBuilder) WithFile(files ...File) {
|
||||||
|
i.dynamicIgnition.Cfg.Storage.Files = append(i.dynamicIgnition.Cfg.Storage.Files, files...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildWithIgnitionFile copies the provided ignition file into the internal
|
||||||
|
// `DynamicIgnition` write path
|
||||||
|
func (i *IgnitionBuilder) BuildWithIgnitionFile(ignPath string) error {
|
||||||
|
inputIgnition, err := os.ReadFile(ignPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(i.dynamicIgnition.WritePath, inputIgnition, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build writes the internal `DynamicIgnition` config to its write path
|
||||||
|
func (i *IgnitionBuilder) Build() error {
|
||||||
|
return i.dynamicIgnition.Write()
|
||||||
|
}
|
||||||
|
@ -64,10 +64,6 @@ func (v *MachineVM) setNewMachineCMD(qemuBinary string, cmdOpts *setNewMachineCM
|
|||||||
// NewMachine initializes an instance of a virtual machine based on the qemu
|
// NewMachine initializes an instance of a virtual machine based on the qemu
|
||||||
// virtualization.
|
// virtualization.
|
||||||
func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
||||||
vmConfigDir, err := machine.GetConfDir(vmtype)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
vm := new(MachineVM)
|
vm := new(MachineVM)
|
||||||
if len(opts.Name) > 0 {
|
if len(opts.Name) > 0 {
|
||||||
vm.Name = opts.Name
|
vm.Name = opts.Name
|
||||||
@ -79,11 +75,9 @@ func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set VM ignition file
|
// set VM ignition file
|
||||||
ignitionFile, err := define.NewMachineFile(filepath.Join(vmConfigDir, vm.Name+".ign"), nil)
|
if err := machine.SetIgnitionFile(&vm.IgnitionFile, vmtype, vm.Name); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
vm.IgnitionFile = *ignitionFile
|
|
||||||
|
|
||||||
// set VM image file
|
// set VM image file
|
||||||
imagePath, err := define.NewMachineFile(opts.ImagePath, nil)
|
imagePath, err := define.NewMachineFile(opts.ImagePath, nil)
|
||||||
|
@ -45,6 +45,23 @@ const (
|
|||||||
dockerConnectTimeout = 5 * time.Second
|
dockerConnectTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// qemuReadyUnit is a unit file that sets up the virtual serial device
|
||||||
|
// where when the VM is done configuring, it will send an ack
|
||||||
|
// so a listening host tknows it can begin interacting with it
|
||||||
|
const qemuReadyUnit = `[Unit]
|
||||||
|
Requires=dev-virtio\\x2dports-%s.device
|
||||||
|
After=remove-moby.service sshd.socket sshd.service
|
||||||
|
After=systemd-user-sessions.service
|
||||||
|
OnFailure=emergency.target
|
||||||
|
OnFailureJobMode=isolate
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/bin/sh -c '/usr/bin/echo Ready >/dev/%s'
|
||||||
|
[Install]
|
||||||
|
RequiredBy=default.target
|
||||||
|
`
|
||||||
|
|
||||||
type MachineVM struct {
|
type MachineVM struct {
|
||||||
// ConfigPath is the path to the configuration file
|
// ConfigPath is the path to the configuration file
|
||||||
ConfigPath define.VMFile
|
ConfigPath define.VMFile
|
||||||
@ -202,50 +219,6 @@ func (v *MachineVM) addMountsToVM(opts machine.InitOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeIgnitionConfigFile generates the ignition config and writes it to the
|
|
||||||
// filesystem
|
|
||||||
func (v *MachineVM) writeIgnitionConfigFile(opts machine.InitOptions, key string) error {
|
|
||||||
ign := &machine.DynamicIgnition{
|
|
||||||
Name: opts.Username,
|
|
||||||
Key: key,
|
|
||||||
VMName: v.Name,
|
|
||||||
VMType: machine.QemuVirt,
|
|
||||||
TimeZone: opts.TimeZone,
|
|
||||||
WritePath: v.getIgnitionFile(),
|
|
||||||
UID: v.UID,
|
|
||||||
Rootful: v.Rootful,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ign.GenerateIgnitionConfig(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ready is a unit file that sets up the virtual serial device
|
|
||||||
// where when the VM is done configuring, it will send an ack
|
|
||||||
// so a listening host knows it can being interacting with it
|
|
||||||
ready := `[Unit]
|
|
||||||
Requires=dev-virtio\\x2dports-%s.device
|
|
||||||
After=remove-moby.service sshd.socket sshd.service
|
|
||||||
After=systemd-user-sessions.service
|
|
||||||
OnFailure=emergency.target
|
|
||||||
OnFailureJobMode=isolate
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
RemainAfterExit=yes
|
|
||||||
ExecStart=/bin/sh -c '/usr/bin/echo Ready >/dev/%s'
|
|
||||||
[Install]
|
|
||||||
RequiredBy=default.target
|
|
||||||
`
|
|
||||||
readyUnit := machine.Unit{
|
|
||||||
Enabled: machine.BoolToPtr(true),
|
|
||||||
Name: "ready.service",
|
|
||||||
Contents: machine.StrToPtr(fmt.Sprintf(ready, "vport1p1", "vport1p1")),
|
|
||||||
}
|
|
||||||
ign.Cfg.Systemd.Units = append(ign.Cfg.Systemd.Units, readyUnit)
|
|
||||||
|
|
||||||
return ign.Write()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init writes the json configuration file to the filesystem for
|
// Init writes the json configuration file to the filesystem for
|
||||||
// other verbs (start, stop)
|
// other verbs (start, stop)
|
||||||
func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
||||||
@ -327,17 +300,35 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
|||||||
logrus.Warn("ignoring init option to disable user-mode networking: this mode is not supported by the QEMU backend")
|
logrus.Warn("ignoring init option to disable user-mode networking: this mode is not supported by the QEMU backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder := machine.NewIgnitionBuilder(machine.DynamicIgnition{
|
||||||
|
Name: opts.Username,
|
||||||
|
Key: key,
|
||||||
|
VMName: v.Name,
|
||||||
|
VMType: machine.QemuVirt,
|
||||||
|
TimeZone: opts.TimeZone,
|
||||||
|
WritePath: v.getIgnitionFile(),
|
||||||
|
UID: v.UID,
|
||||||
|
Rootful: v.Rootful,
|
||||||
|
})
|
||||||
|
|
||||||
// If the user provides an ignition file, we need to
|
// If the user provides an ignition file, we need to
|
||||||
// copy it into the conf dir
|
// copy it into the conf dir
|
||||||
if len(opts.IgnitionPath) > 0 {
|
if len(opts.IgnitionPath) > 0 {
|
||||||
inputIgnition, err := os.ReadFile(opts.IgnitionPath)
|
return false, builder.BuildWithIgnitionFile(opts.IgnitionPath)
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return false, os.WriteFile(v.getIgnitionFile(), inputIgnition, 0644)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = v.writeIgnitionConfigFile(opts, key)
|
if err := builder.GenerateIgnitionConfig(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
readyUnit := machine.Unit{
|
||||||
|
Enabled: machine.BoolToPtr(true),
|
||||||
|
Name: "ready.service",
|
||||||
|
Contents: machine.StrToPtr(fmt.Sprintf(qemuReadyUnit, "vport1p1", "vport1p1")),
|
||||||
|
}
|
||||||
|
builder.WithUnit(readyUnit)
|
||||||
|
|
||||||
|
err = builder.Build()
|
||||||
callbackFuncs.Add(v.IgnitionFile.Delete)
|
callbackFuncs.Add(v.IgnitionFile.Delete)
|
||||||
|
|
||||||
return err == nil, err
|
return err == nil, err
|
||||||
|
Reference in New Issue
Block a user