nuageinit: implement power_state_change and locale support

This commit is contained in:
Baptiste Daroussin
2026-06-05 22:48:18 +02:00
parent be711ade6f
commit 328a76d17f
3 changed files with 166 additions and 0 deletions
+61
View File
@@ -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
+49
View File
@@ -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"
+56
View File
@@ -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
}