nuageinit: implement mounts support

Add support for the 'mounts' cloud-config key which configures
mount points by appending entries to /etc/fstab and creating
the corresponding directories.
This commit is contained in:
Baptiste Daroussin
2026-06-05 12:05:08 +02:00
parent 6e54d00867
commit 797dad91ff
4 changed files with 144 additions and 1 deletions
+47 -1
View File
@@ -821,6 +821,50 @@ local function addfile(file, defer)
return true
end
local function add_fstab_entry(root, device, mount_point, fstype, options, dump_freq, passno)
local fstab_path = root .. "/etc/fstab"
local f = io.open(fstab_path, "a")
if not f then
warnmsg("unable to open " .. fstab_path .. " for writing")
return false
end
options = options or "rw"
dump_freq = dump_freq or 0
passno = passno or 0
f:write(string.format("%s\t\t%s\t\t%s\t\t%s\t\t%d\t\t%d\n",
device, mount_point, fstype, options, dump_freq, passno))
f:close()
return true
end
local function remove_fstab_entry(root, mount_point)
local fstab_path = root .. "/etc/fstab"
local f = io.open(fstab_path, "r")
if not f then
return
end
local lines = {}
for line in f:lines() do
local fields = {}
for field in line:gmatch("%S+") do
table.insert(fields, field)
end
if fields[2] ~= mount_point then
table.insert(lines, line)
end
end
f:close()
local nf = io.open(fstab_path, "w")
if not nf then
warnmsg("unable to open " .. fstab_path .. " for writing")
return
end
for _, line in ipairs(lines) do
nf:write(line .. "\n")
end
nf:close()
end
local n = {
shell_escape = shell_escape,
warn = warnmsg,
@@ -844,7 +888,9 @@ local n = {
upgrade_packages = upgrade_packages,
addsudo = addsudo,
adddoas = adddoas,
addfile = addfile
addfile = addfile,
add_fstab_entry = add_fstab_entry,
remove_fstab_entry = remove_fstab_entry,
}
return n
+33
View File
@@ -531,6 +531,38 @@ local function disable_root(obj)
end
end
local function mounts(obj)
if obj.mounts == nil then return end
for _, m in ipairs(obj.mounts) do
local device, mount_point, fstype, options, dump_freq, passno
if type(m) == "table" then
if m[1] then
-- List format: [device, mount_point, fstype, options, dump_freq, passno]
device = m[1]
mount_point = m[2]
fstype = m[3]
options = m[4]
dump_freq = tonumber(m[5]) or 0
passno = tonumber(m[6]) or 0
else
-- Dict format
device = m.name or m.device or m.spec
mount_point = m.mount_point or m.mountpoint
fstype = m.type or m.filesystem or m.fstype
options = m.options or m.opts
dump_freq = tonumber(m.dump) or 0
passno = tonumber(m.passno) or tonumber(m.pass_no) or 0
end
end
if device and mount_point and fstype then
nuage.mkdir_p(root .. mount_point)
nuage.add_fstab_entry(root, device, mount_point, fstype, options, dump_freq, passno)
else
warnmsg("Invalid mount entry, skipping")
end
end
end
local function bootcmd(obj)
if obj.bootcmd == nil then return end
local f = nil
@@ -828,6 +860,7 @@ if line == nil then
-- YAML user-data
elseif line == "#cloud-config" then
local pre_network_calls = {
mounts,
bootcmd,
sethostname,
manage_etc_hosts,
+36
View File
@@ -159,6 +159,42 @@ Defaults to
Set to
.Ar false
to skip this behaviour.
.It Ic mounts
A list of mount points to configure.
Each entry is written to
.Pa /etc/fstab
and the mount point directory is created.
.Pp
Each entry can be specified as a list:
.Bd -literal -offset indent
[ device, mountpoint, fstype ]
.Ed
.Pp
or as an object:
.Bd -literal -offset indent
{ device: "...", mountpoint: "...", type: "...", options: "..." }
.Ed
.Pp
The following keys are recognized:
.Bl -tag -width "options"
.It device (or name, spec)
The device to mount.
.It mountpoint (or mount_point)
The mount point directory.
.It type (or fstype, filesystem)
The filesystem type.
.It options (or opts)
The mount options, defaults to
.Qq rw .
.It dump
The dump frequency for
.Xr dump 8 ,
defaults to 0.
.It passno
The pass number for
.Xr fsck 8 ,
defaults to 0.
.El
.It Ic timezone
Sets the system timezone based on the value provided.
.Pp
+28
View File
@@ -34,6 +34,7 @@ atf_test_case config2_userdata_ssh_deletekeys
atf_test_case config2_userdata_disable_root
atf_test_case config2_userdata_bootcmd
atf_test_case config2_userdata_manage_etc_hosts
atf_test_case config2_userdata_mounts
atf_test_case config2_userdata_fqdn_and_hostname
atf_test_case config2_userdata_write_files
@@ -1113,6 +1114,32 @@ EOF
atf_check -o inline:"::1\t\tlocalhost\n127.0.0.1\t\tlocalhost\n" cat etc/hosts
}
config2_userdata_mounts_head()
{
atf_set "require.user" root
}
config2_userdata_mounts_body()
{
mkdir -p media/nuageinit
setup_test_adduser
printf "{}" > media/nuageinit/meta_data.json
cat > media/nuageinit/user_data <<EOF
#cloud-config
mounts:
- [ /dev/ada1p1, /mnt/data, ufs, rw, 0, 2 ]
- device: tmpfs
mountpoint: /mnt/tmp
fstype: tmpfs
options: "size=256M"
EOF
atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
atf_check -o match:"/dev/ada1p1.*/mnt/data.*ufs.*rw.*0.*2" cat etc/fstab
atf_check -o match:"tmpfs.*/mnt/tmp.*tmpfs.*size=256M.*0.*0" cat etc/fstab
test -d mnt/data || atf_fail "/mnt/data directory not created"
test -d mnt/tmp || atf_fail "/mnt/tmp directory not created"
true
}
config2_userdata_fqdn_and_hostname_body()
{
mkdir -p media/nuageinit
@@ -1162,6 +1189,7 @@ atf_init_test_cases()
atf_add_test_case config2_userdata_disable_root
atf_add_test_case config2_userdata_bootcmd
atf_add_test_case config2_userdata_manage_etc_hosts
atf_add_test_case config2_userdata_mounts
atf_add_test_case config2_userdata_fqdn_and_hostname
atf_add_test_case config2_userdata_write_files
}