Skip to content

Commit 1bab40c

Browse files
committed
machine: share virtiofs systemd unit generation
Make GenerateSystemDFilesForVirtiofsMounts an agnostic function and move the code from apple to pkg/machine/volume_systemd.go. MountVolumesToVM is now a no-op matching the AppleHV and LibKrun behaviour. Continues from PR: podman-container-tools#28736 Fixes: https://redhat.atlassian.net/browse/RUN-4742 Signed-off-by: Nicola Sella <nsella@redhat.com>
1 parent 62111c7 commit 1bab40c

5 files changed

Lines changed: 108 additions & 123 deletions

File tree

pkg/machine/apple/apple.go

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,9 @@ import (
1717
"github.com/sirupsen/logrus"
1818
"go.podman.io/common/pkg/config"
1919
"go.podman.io/common/pkg/strongunits"
20-
"go.podman.io/podman/v6/pkg/machine"
2120
"go.podman.io/podman/v6/pkg/machine/define"
22-
"go.podman.io/podman/v6/pkg/machine/ignition"
2321
"go.podman.io/podman/v6/pkg/machine/sockets"
2422
"go.podman.io/podman/v6/pkg/machine/vmconfigs"
25-
"go.podman.io/podman/v6/pkg/systemd/parser"
2623
)
2724

2825
const applehvMACAddress = "5a:94:ef:e4:0c:ee"
@@ -65,80 +62,6 @@ func SetProviderAttrs(mc *vmconfigs.MachineConfig, opts define.SetOptions, state
6562
return nil
6663
}
6764

68-
func GenerateSystemDFilesForVirtiofsMounts(mounts []machine.VirtIoFs) ([]ignition.Unit, error) {
69-
// mounting in fcos with virtiofs is a bit of a dance. we need a unit file for the mount, a unit file
70-
// for automatic mounting on boot, and a "preparatory" service file that disables FCOS security, performs
71-
// the mkdir of the mount point, and then re-enables security. This must be done for each mount.
72-
73-
unitFiles := make([]ignition.Unit, 0, len(mounts))
74-
for _, mnt := range mounts {
75-
// Create mount unit for each mount
76-
mountUnit := parser.NewUnitFile()
77-
mountUnit.Add("Mount", "What", "%s")
78-
mountUnit.Add("Mount", "Where", "%s")
79-
mountUnit.Add("Mount", "Type", "virtiofs")
80-
mountUnit.Add("Mount", "Options", fmt.Sprintf("context=\"%s\"", machine.NFSSELinuxContext))
81-
mountUnit.Add("Install", "WantedBy", "local-fs.target")
82-
mountUnitFile, err := mountUnit.ToString()
83-
if err != nil {
84-
return nil, err
85-
}
86-
87-
virtiofsMount := ignition.Unit{
88-
Enabled: ignition.BoolToPtr(true),
89-
Name: fmt.Sprintf("%s.mount", parser.PathEscape(mnt.Target)),
90-
Contents: ignition.StrToPtr(fmt.Sprintf(mountUnitFile, mnt.Tag, mnt.Target)),
91-
}
92-
93-
unitFiles = append(unitFiles, virtiofsMount)
94-
}
95-
96-
// This is a way to workaround the FCOS limitation of creating directories
97-
// at the rootfs / and then mounting to them.
98-
immutableRootOff := parser.NewUnitFile()
99-
immutableRootOff.Add("Unit", "Description", "Allow systemd to create mount points on /")
100-
immutableRootOff.Add("Unit", "DefaultDependencies", "no")
101-
102-
immutableRootOff.Add("Service", "Type", "oneshot")
103-
immutableRootOff.Add("Service", "ExecStart", "chattr -i /")
104-
105-
immutableRootOff.Add("Install", "WantedBy", "local-fs-pre.target")
106-
immutableRootOffFile, err := immutableRootOff.ToString()
107-
if err != nil {
108-
return nil, err
109-
}
110-
111-
immutableRootOffUnit := ignition.Unit{
112-
Contents: ignition.StrToPtr(immutableRootOffFile),
113-
Name: "immutable-root-off.service",
114-
Enabled: ignition.BoolToPtr(true),
115-
}
116-
unitFiles = append(unitFiles, immutableRootOffUnit)
117-
118-
immutableRootOn := parser.NewUnitFile()
119-
immutableRootOn.Add("Unit", "Description", "Set / back to immutable after mounts are done")
120-
immutableRootOn.Add("Unit", "DefaultDependencies", "no")
121-
immutableRootOn.Add("Unit", "After", "local-fs.target")
122-
123-
immutableRootOn.Add("Service", "Type", "oneshot")
124-
immutableRootOn.Add("Service", "ExecStart", "chattr +i /")
125-
126-
immutableRootOn.Add("Install", "WantedBy", "local-fs.target")
127-
immutableRootOnFile, err := immutableRootOn.ToString()
128-
if err != nil {
129-
return nil, err
130-
}
131-
132-
immutableRootOnUnit := ignition.Unit{
133-
Contents: ignition.StrToPtr(immutableRootOnFile),
134-
Name: "immutable-root-on.service",
135-
Enabled: ignition.BoolToPtr(true),
136-
}
137-
unitFiles = append(unitFiles, immutableRootOnUnit)
138-
139-
return unitFiles, nil
140-
}
141-
14265
// StartGenericAppleVM is wrapped by apple provider methods and starts the vm
14366
func StartGenericAppleVM(mc *vmconfigs.MachineConfig, cmdBinary string, bootloader vfConfig.Bootloader, endpoint string) (func() error, func() error, error) {
14467
var ignitionSocket *define.VMFile

pkg/machine/applehv/stubber.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (a *AppleHVStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.Machin
5858
}
5959

6060
// Populate the ignition file with virtiofs stuff
61-
virtIOIgnitionMounts, err := apple.GenerateSystemDFilesForVirtiofsMounts(virtiofsMounts)
61+
virtIOIgnitionMounts, err := machine.GenerateSystemDFilesForVirtiofsMounts(virtiofsMounts)
6262
if err != nil {
6363
return err
6464
}

pkg/machine/libkrun/stubber.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func (l *LibKrunStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.Machin
4545
}
4646

4747
// Populate the ignition file with virtiofs stuff
48-
virtIOIgnitionMounts, err := apple.GenerateSystemDFilesForVirtiofsMounts(virtiofsMounts)
48+
virtIOIgnitionMounts, err := machine.GenerateSystemDFilesForVirtiofsMounts(virtiofsMounts)
4949
if err != nil {
5050
return err
5151
}

pkg/machine/qemu/stubber.go

Lines changed: 16 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"os"
1111
"os/exec"
1212
"strconv"
13-
"strings"
1413
"time"
1514

1615
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
@@ -89,7 +88,7 @@ func (q *QEMUStubber) setQEMUCommandLine(mc *vmconfigs.MachineConfig) error {
8988
return nil
9089
}
9190

92-
func (q *QEMUStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, _ *ignition.IgnitionBuilder) error {
91+
func (q *QEMUStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, ignBuilder *ignition.IgnitionBuilder) error {
9392
monitor, err := command.NewQMPMonitor(opts.Name, opts.Dirs.RuntimeDir)
9493
if err != nil {
9594
return err
@@ -110,6 +109,19 @@ func (q *QEMUStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineCo
110109

111110
mc.QEMUHypervisor = &qemuConfig
112111
mc.QEMUHypervisor.QEMUPidPath = qemuPidPath
112+
113+
// Populate the ignition config with systemd unit files for virtiofs mounts,
114+
// mirroring the approach used by AppleHV and LibKrun providers.
115+
virtiofsMounts := make([]machine.VirtIoFs, 0, len(mc.Mounts))
116+
for _, mnt := range mc.Mounts {
117+
virtiofsMounts = append(virtiofsMounts, machine.MountToVirtIOFs(mnt))
118+
}
119+
virtIOIgnitionMounts, err := machine.GenerateSystemDFilesForVirtiofsMounts(virtiofsMounts)
120+
if err != nil {
121+
return err
122+
}
123+
ignBuilder.WithUnit(virtIOIgnitionMounts...)
124+
113125
return q.resizeDisk(mc.Resources.DiskSize, mc.ImagePath)
114126
}
115127

@@ -334,48 +346,8 @@ func (q *QEMUStubber) RemoveAndCleanMachines(_ *define.MachineDirs) error {
334346
return nil
335347
}
336348

337-
// mountVolumesToVM iterates through the machine's volumes and mounts them to the
338-
// machine
339-
// TODO this should probably be temporary; mount code should probably be its own package and shared completely
340-
func (q *QEMUStubber) MountVolumesToVM(mc *vmconfigs.MachineConfig, quiet bool) error {
341-
for _, mount := range mc.Mounts {
342-
if !quiet {
343-
fmt.Printf("Mounting volume... %s:%s\n", mount.Source, mount.Target)
344-
}
345-
// create mountpoint directory if it doesn't exist
346-
// because / is immutable, we have to monkey around with permissions
347-
// if we dont mount in /home or /mnt
348-
var args []string
349-
if !strings.HasPrefix(mount.Target, "/home") && !strings.HasPrefix(mount.Target, "/mnt") {
350-
args = append(args, "sudo", "chattr", "-i", "/", ";")
351-
}
352-
args = append(args, "sudo", "mkdir", "-p", strconv.Quote(mount.Target))
353-
if !strings.HasPrefix(mount.Target, "/home") && !strings.HasPrefix(mount.Target, "/mnt") {
354-
args = append(args, ";", "sudo", "chattr", "+i", "/", ";")
355-
}
356-
err := machine.LocalhostSSH(mc.SSH.RemoteUsername, mc.SSH.IdentityPath, mc.Name, mc.SSH.Port, args)
357-
if err != nil {
358-
return err
359-
}
360-
361-
mountFlags := fmt.Sprintf("context=\"%s\"", machine.NFSSELinuxContext)
362-
if mount.ReadOnly {
363-
mountFlags += ",ro"
364-
}
365-
// NOTE: The mount type q.Type was previously serialized as 9p for older Linux versions,
366-
// but we ignore it now because we want the mount type to be dynamic, not static. Or
367-
// in other words we don't want to make people unnecessarily reprovision their machines
368-
// to upgrade from 9p to virtiofs.
369-
mountOptions := []string{
370-
"-t", "virtiofs",
371-
mount.Tag, strconv.Quote(mount.Target),
372-
"-o", mountFlags,
373-
}
374-
err = machine.LocalhostSSH(mc.SSH.RemoteUsername, mc.SSH.IdentityPath, mc.Name, mc.SSH.Port, append([]string{"sudo", "mount"}, mountOptions...))
375-
if err != nil {
376-
return err
377-
}
378-
}
349+
func (q *QEMUStubber) MountVolumesToVM(_ *vmconfigs.MachineConfig, _ bool) error {
350+
// virtiofs: mounts are handled by systemd units baked into ignition at init time
379351
return nil
380352
}
381353

pkg/machine/volume_systemd.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package machine
2+
3+
import (
4+
"fmt"
5+
6+
"go.podman.io/podman/v6/pkg/machine/ignition"
7+
"go.podman.io/podman/v6/pkg/systemd/parser"
8+
)
9+
10+
// GenerateSystemDFilesForVirtiofsMounts generates the systemd unit files needed
11+
// to mount virtiofs volumes inside a FCOS guest VM. It is shared between the
12+
// AppleHV, LibKrun, and QEMU providers.
13+
//
14+
// For each mount it produces one systemd .mount unit. In addition, two helper
15+
// services are emitted once regardless of the number of mounts:
16+
//
17+
// - immutable-root-off.service (runs before local-fs-pre.target):
18+
// transiently removes the immutable flag from / so systemd can create the
19+
// mount-point directories.
20+
// - immutable-root-on.service (runs after local-fs.target):
21+
// re-applies the immutable flag after all mounts are active.
22+
func GenerateSystemDFilesForVirtiofsMounts(mounts []VirtIoFs) ([]ignition.Unit, error) {
23+
unitFiles := make([]ignition.Unit, 0, len(mounts)+2)
24+
for _, mnt := range mounts {
25+
// Create mount unit for each mount
26+
mountUnit := parser.NewUnitFile()
27+
mountUnit.Add("Mount", "What", "%s")
28+
mountUnit.Add("Mount", "Where", "%s")
29+
mountUnit.Add("Mount", "Type", "virtiofs")
30+
mountUnit.Add("Mount", "Options", fmt.Sprintf("context=\"%s\"", NFSSELinuxContext))
31+
mountUnit.Add("Install", "WantedBy", "local-fs.target")
32+
mountUnitFile, err := mountUnit.ToString()
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
virtiofsMount := ignition.Unit{
38+
Enabled: ignition.BoolToPtr(true),
39+
Name: fmt.Sprintf("%s.mount", parser.PathEscape(mnt.Target)),
40+
Contents: ignition.StrToPtr(fmt.Sprintf(mountUnitFile, mnt.Tag, mnt.Target)),
41+
}
42+
43+
unitFiles = append(unitFiles, virtiofsMount)
44+
}
45+
46+
// This is a way to workaround the FCOS limitation of creating directories
47+
// at the rootfs / and then mounting to them.
48+
immutableRootOff := parser.NewUnitFile()
49+
immutableRootOff.Add("Unit", "Description", "Allow systemd to create mount points on /")
50+
immutableRootOff.Add("Unit", "DefaultDependencies", "no")
51+
52+
immutableRootOff.Add("Service", "Type", "oneshot")
53+
immutableRootOff.Add("Service", "ExecStart", "chattr -i /")
54+
55+
immutableRootOff.Add("Install", "WantedBy", "local-fs-pre.target")
56+
immutableRootOffFile, err := immutableRootOff.ToString()
57+
if err != nil {
58+
return nil, err
59+
}
60+
61+
immutableRootOffUnit := ignition.Unit{
62+
Contents: ignition.StrToPtr(immutableRootOffFile),
63+
Name: "immutable-root-off.service",
64+
Enabled: ignition.BoolToPtr(true),
65+
}
66+
unitFiles = append(unitFiles, immutableRootOffUnit)
67+
68+
immutableRootOn := parser.NewUnitFile()
69+
immutableRootOn.Add("Unit", "Description", "Set / back to immutable after mounts are done")
70+
immutableRootOn.Add("Unit", "DefaultDependencies", "no")
71+
immutableRootOn.Add("Unit", "After", "local-fs.target")
72+
73+
immutableRootOn.Add("Service", "Type", "oneshot")
74+
immutableRootOn.Add("Service", "ExecStart", "chattr +i /")
75+
76+
immutableRootOn.Add("Install", "WantedBy", "local-fs.target")
77+
immutableRootOnFile, err := immutableRootOn.ToString()
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
immutableRootOnUnit := ignition.Unit{
83+
Contents: ignition.StrToPtr(immutableRootOnFile),
84+
Name: "immutable-root-on.service",
85+
Enabled: ignition.BoolToPtr(true),
86+
}
87+
unitFiles = append(unitFiles, immutableRootOnUnit)
88+
89+
return unitFiles, nil
90+
}

0 commit comments

Comments
 (0)