Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 06 Jun 2026 06:14:18 +0000
From:      Baptiste Daroussin <bapt@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 328a76d17f85 - main - nuageinit: implement power_state_change and locale support
Message-ID:  <6a23baba.2757f.6cff3de7@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=328a76d17f85ff6aa6228035c4c4b989eb7534f8

commit 328a76d17f85ff6aa6228035c4c4b989eb7534f8
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2026-06-05 20:48:18 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2026-06-05 20:48:18 +0000

    nuageinit: implement power_state_change and locale support
---
 libexec/nuageinit/nuageinit          | 61 ++++++++++++++++++++++++++++++++++++
 libexec/nuageinit/nuageinit.7        | 49 +++++++++++++++++++++++++++++
 libexec/nuageinit/tests/nuageinit.sh | 56 +++++++++++++++++++++++++++++++++
 3 files changed, 166 insertions(+)

diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit
index bd72f02d4503..8a9c5c022862 100755
--- a/libexec/nuageinit/nuageinit
+++ b/libexec/nuageinit/nuageinit
@@ -637,6 +637,28 @@ local function keyboard(obj)
 	f:close()
 end
 
+local function locale(obj)
+	if obj.locale == nil then return end
+	local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+	if not root then root = "" end
+	local profile_path = root .. "/etc/profile"
+	local f = io.open(profile_path, "a")
+	if not f then
+		nuage.warn("unable to open " .. profile_path .. " for writing")
+		return
+	end
+	if type(obj.locale) == "string" then
+		f:write("export LANG=" .. obj.locale .. "\n")
+	elseif type(obj.locale) == "table" then
+		for k, v in pairs(obj.locale) do
+			f:write("export " .. k .. "=" .. v .. "\n")
+		end
+	else
+		nuage.warn("locale: invalid type " .. type(obj.locale) .. ", expecting string or object")
+	end
+	f:close()
+end
+
 local function mounts(obj)
 	if obj.mounts == nil then return end
 	for _, m in ipairs(obj.mounts) do
@@ -725,6 +747,43 @@ local function packages(obj)
 	end
 end
 
+local function power_state_change(obj)
+	if obj.power_state == nil then return end
+	local ps = obj.power_state
+	local delay = ps.delay or "now"
+	local mode = ps.mode or "poweroff"
+	local message = ps.message
+	local condition = ps.condition
+	if condition == nil then condition = true end
+
+	-- Evaluate condition
+	if condition == false then return end
+	if type(condition) == "string" then
+		local ret = os.execute(condition)
+		if not ret then return end
+	end
+
+	-- Map mode to shutdown flag
+	local mode_map = {poweroff = "p", reboot = "r", halt = "h"}
+	local flag = mode_map[mode]
+	if not flag then
+		nuage.warn("power_state: invalid mode '" .. mode .. "', using poweroff")
+		flag = "p"
+	end
+
+	-- Build shutdown command
+	local cmd = "shutdown -" .. flag .. " " .. delay
+	if message then
+		cmd = cmd .. " " .. nuage.shell_escape(message)
+	end
+
+	if os.getenv("NUAGE_RUN_TESTS") then
+		print(cmd)
+		return
+	end
+	os.execute(cmd)
+end
+
 local function chpasswd(obj)
 	if obj.chpasswd == nil then return end
 	nuage.chpasswd(obj.chpasswd)
@@ -1018,6 +1077,7 @@ elseif line == "#cloud-config" then
 		network_config,
 		resolv_conf,
 		keyboard,
+		locale,
 		disable_root,
 		ssh_pwauth,
 		runcmd,
@@ -1030,6 +1090,7 @@ elseif line == "#cloud-config" then
 		users,
 		chpasswd,
 		write_files_deferred,
+		power_state_change,
 	}
 
 	local calls_table = pre_network_calls
diff --git a/libexec/nuageinit/nuageinit.7 b/libexec/nuageinit/nuageinit.7
index b4be4e4b2d58..6d0f8ae2f41c 100644
--- a/libexec/nuageinit/nuageinit.7
+++ b/libexec/nuageinit/nuageinit.7
@@ -212,6 +212,16 @@ A list of IP/netmask sortlist entries.
 .It options
 A dictionary of resolver options.
 .El
+.It Ic locale
+Set the system locale by appending
+.Qq Cm export
+statements to
+.Pa /etc/profile .
+.Pp
+If the value is a string, it is used as the
+.Dq Cm LANG
+value.
+If the value is an object mapping, each key-value pair is exported.
 .It Ic keyboard
 An object configuring the keyboard layout.
 .Pp
@@ -442,6 +452,45 @@ List of packages to be installed.
 Update the remote package metadata.
 .It Ic package_upgrade
 Upgrade the packages installed to their latest version.
+.It Ic power_state
+An object controlling the power state of the instance after configuration.
+The following keys are recognized:
+.Bl -tag -width "condition"
+.It Ic delay
+Time to wait before the action.
+Can be
+.Qq now
+or a time accepted by
+.Xr shutdown 8
+(e.g.,
+.Qq +5
+for five minutes).
+Defaults to
+.Qq now .
+.It Ic mode
+The action to take:
+.Qq poweroff ,
+.Qq reboot ,
+or
+.Qq halt .
+Defaults to
+.Qq poweroff .
+.It Ic message
+Optional message to display to users.
+.It Ic timeout
+Not supported on
+.Fx ,
+silently ignored.
+.It Ic condition
+Boolean or command string.
+If
+.Qq false ,
+the action is skipped.
+If a string, it is run as a command; the action is only taken if the command
+succeeds.
+Defaults to
+.Qq true .
+.El
 .It Ic users
 Specify a list of users to be created:
 .Bl -tag -width "ssh_authorized_keys"
diff --git a/libexec/nuageinit/tests/nuageinit.sh b/libexec/nuageinit/tests/nuageinit.sh
index 4b751dd2ca43..21cd2e8f17c5 100644
--- a/libexec/nuageinit/tests/nuageinit.sh
+++ b/libexec/nuageinit/tests/nuageinit.sh
@@ -41,6 +41,8 @@ atf_test_case config2_userdata_ssh_authkey_fingerprints
 atf_test_case config2_userdata_ntp
 atf_test_case config2_userdata_ca_certs
 atf_test_case config2_userdata_multipart
+atf_test_case config2_userdata_power_state
+atf_test_case config2_userdata_locale
 atf_test_case config2_userdata_fqdn_and_hostname
 atf_test_case config2_userdata_write_files
 
@@ -1308,6 +1310,58 @@ EOF
 	true
 }
 
+config2_userdata_power_state_head()
+{
+	atf_set "require.user" root
+}
+config2_userdata_power_state_body()
+{
+	mkdir -p media/nuageinit
+	setup_test_adduser
+	export NUAGE_RUN_TESTS=1
+	printf "{}" > media/nuageinit/meta_data.json
+	cat > media/nuageinit/user_data <<EOF
+#cloud-config
+power_state:
+  delay: "+5"
+  mode: reboot
+  message: "Rebooting after configuration is complete"
+  timeout: 30
+  condition: true
+EOF
+	atf_check -o inline:"shutdown -r +5 'Rebooting after configuration is complete'\n" \
+	    /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet
+	true
+}
+
+config2_userdata_locale_head()
+{
+	atf_set "require.user" root
+}
+config2_userdata_locale_body()
+{
+	mkdir -p media/nuageinit
+	setup_test_adduser
+	printf "{}" > media/nuageinit/meta_data.json
+	cat > media/nuageinit/user_data <<EOF
+#cloud-config
+locale: fr_FR.UTF-8
+EOF
+	atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+	atf_check -o inline:"export LANG=fr_FR.UTF-8\n" cat etc/profile
+
+	cat > media/nuageinit/user_data <<EOF
+#cloud-config
+locale:
+  LANG: de_DE.UTF-8
+  LC_ALL: de_DE.UTF-8
+EOF
+	atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
+	atf_check -o match:"export LANG=de_DE.UTF-8" cat etc/profile
+	atf_check -o match:"export LC_ALL=de_DE.UTF-8" cat etc/profile
+	true
+}
+
 config2_userdata_fqdn_and_hostname_body()
 {
 	mkdir -p media/nuageinit
@@ -1364,6 +1418,8 @@ atf_init_test_cases()
 	atf_add_test_case config2_userdata_ntp
 	atf_add_test_case config2_userdata_ca_certs
 	atf_add_test_case config2_userdata_multipart
+	atf_add_test_case config2_userdata_power_state
+	atf_add_test_case config2_userdata_locale
 	atf_add_test_case config2_userdata_fqdn_and_hostname
 	atf_add_test_case config2_userdata_write_files
 }


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a23baba.2757f.6cff3de7>