The branch main has been updated by bapt:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=797dad91ff468a9bd6cd5d4f720eb4bbac1f454a

commit 797dad91ff468a9bd6cd5d4f720eb4bbac1f454a
Author:     Baptiste Daroussin <[email protected]>
AuthorDate: 2026-06-05 10:05:08 +0000
Commit:     Baptiste Daroussin <[email protected]>
CommitDate: 2026-06-05 11:15:16 +0000

    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.
---
 libexec/nuageinit/nuage.lua          | 48 +++++++++++++++++++++++++++++++++++-
 libexec/nuageinit/nuageinit          | 33 +++++++++++++++++++++++++
 libexec/nuageinit/nuageinit.7        | 36 +++++++++++++++++++++++++++
 libexec/nuageinit/tests/nuageinit.sh | 28 +++++++++++++++++++++
 4 files changed, 144 insertions(+), 1 deletion(-)

diff --git a/libexec/nuageinit/nuage.lua b/libexec/nuageinit/nuage.lua
index 45e14ef0ce21..34cf8c9fc090 100644
--- a/libexec/nuageinit/nuage.lua
+++ b/libexec/nuageinit/nuage.lua
@@ -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
diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit
index 9d2f3c6024c4..f56d1207cf01 100755
--- a/libexec/nuageinit/nuageinit
+++ b/libexec/nuageinit/nuageinit
@@ -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,
diff --git a/libexec/nuageinit/nuageinit.7 b/libexec/nuageinit/nuageinit.7
index 66a72324f414..c2981669acb7 100644
--- a/libexec/nuageinit/nuageinit.7
+++ b/libexec/nuageinit/nuageinit.7
@@ -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
diff --git a/libexec/nuageinit/tests/nuageinit.sh 
b/libexec/nuageinit/tests/nuageinit.sh
index ac7086183d86..11d34fcd98ea 100644
--- a/libexec/nuageinit/tests/nuageinit.sh
+++ b/libexec/nuageinit/tests/nuageinit.sh
@@ -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
 }

Reply via email to