nuageinit: implement MIME multipart user-data support
Add support for MIME multipart/mixed user-data, allowing a single user-data blob to contain multiple parts with different content types.
This commit is contained in:
@@ -896,6 +896,50 @@ local function remove_fstab_entry(root, mount_point)
|
||||
nf:close()
|
||||
end
|
||||
|
||||
local function parse_mime_multipart(data)
|
||||
local boundary = data:match("boundary=\"([^\"]+)\"")
|
||||
if not boundary then
|
||||
boundary = data:match("boundary=([^%s;]+)")
|
||||
end
|
||||
if not boundary then
|
||||
return nil
|
||||
end
|
||||
local parts = {}
|
||||
local pos = data:find("\n") or 1
|
||||
local first = data:find("--" .. boundary, pos, true)
|
||||
if not first then
|
||||
return nil
|
||||
end
|
||||
pos = data:find("\n", first)
|
||||
if not pos then return nil end
|
||||
pos = pos + 1
|
||||
while true do
|
||||
local nextb = data:find("--" .. boundary, pos, true)
|
||||
if not nextb then break end
|
||||
local part = data:sub(pos, nextb - 1)
|
||||
part = part:gsub("^\r?\n", ""):gsub("\r?\n$", "")
|
||||
local header_end = part:find("\r?\n\r?\n")
|
||||
local headers_str, body
|
||||
if header_end then
|
||||
headers_str = part:sub(1, header_end - 1)
|
||||
body = part:sub(header_end + 2):gsub("^\r?\n", ""):gsub("\r?\n$", "")
|
||||
else
|
||||
body = part
|
||||
end
|
||||
local ct = "text/plain"
|
||||
if headers_str then
|
||||
local m = headers_str:match("[Cc]ontent%-[Tt]ype:%s*([^%s;]+)")
|
||||
if m then ct = m:lower() end
|
||||
end
|
||||
table.insert(parts, {content_type = ct, body = body})
|
||||
local after = data:sub(nextb + 2 + #boundary, nextb + 3 + #boundary)
|
||||
if after == "--" then break end
|
||||
pos = data:find("\n", nextb) or nextb
|
||||
if pos then pos = pos + 1 end
|
||||
end
|
||||
return parts
|
||||
end
|
||||
|
||||
local n = {
|
||||
shell_escape = shell_escape,
|
||||
warn = warnmsg,
|
||||
@@ -923,6 +967,7 @@ local n = {
|
||||
add_fstab_entry = add_fstab_entry,
|
||||
remove_fstab_entry = remove_fstab_entry,
|
||||
write_resolv_conf = write_resolv_conf,
|
||||
parse_mime_multipart = parse_mime_multipart,
|
||||
}
|
||||
|
||||
return n
|
||||
|
||||
@@ -915,6 +915,44 @@ local function load_userdata()
|
||||
f:close()
|
||||
return
|
||||
end
|
||||
if line:match("^Content%-Type: multipart/") then
|
||||
local rest = f:read("*a")
|
||||
f:close()
|
||||
local full = line .. "\n" .. rest
|
||||
local parts = nuage.parse_mime_multipart(full)
|
||||
if parts then
|
||||
local cc_body = nil
|
||||
for _, p in ipairs(parts) do
|
||||
if p.content_type == "text/cloud-config" then
|
||||
cc_body = p.body
|
||||
elseif p.content_type:match("x%-shellscript") or p.content_type:match("x%-sh") then
|
||||
if citype ~= "postnet" then
|
||||
nuage.mkdir_p(root .. "/var/cache/nuageinit")
|
||||
local spath = root .. "/var/cache/nuageinit/multipart_script"
|
||||
local sf = io.open(spath, "w")
|
||||
if sf then
|
||||
sf:write(p.body .. "\n")
|
||||
sf:close()
|
||||
nuage.chmod(spath, "0755")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if cc_body then
|
||||
local obj = yaml.load(cc_body)
|
||||
if obj then
|
||||
if citype ~= "postnet" then
|
||||
nuage.mkdir_p(root .. "/var/cache/nuageinit")
|
||||
local tof = assert(io.open(root .. "/var/cache/nuageinit/user_data", "w"))
|
||||
tof:write("#cloud-config\n" .. cc_body)
|
||||
tof:close()
|
||||
end
|
||||
return "#cloud-config", obj
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
if citype ~= "postnet" then
|
||||
local content = f:read("*a")
|
||||
if not content or #string.gsub(content, "^%s*(.-)%s*$", "%1") == 0 then
|
||||
|
||||
@@ -551,6 +551,20 @@ A boolean to specify that the files should be created after the packages are
|
||||
installed and the users are created.
|
||||
.El
|
||||
.El
|
||||
.Pp
|
||||
Additionally, user-data can be provided as a MIME multipart message
|
||||
with content type
|
||||
.Qq multipart/mixed .
|
||||
Each part is handled according to its
|
||||
.Qq Content-Type
|
||||
header.
|
||||
Supported part types:
|
||||
.Bl -tag -width "text/x-shellscript"
|
||||
.It text/cloud-config
|
||||
Processed as a cloud-config YAML document.
|
||||
.It text/x-shellscript
|
||||
Saved as an executable script for later execution.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
Here is an example of a YAML configuration for
|
||||
.Nm :
|
||||
|
||||
@@ -40,6 +40,7 @@ atf_test_case config2_userdata_keyboard
|
||||
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_fqdn_and_hostname
|
||||
atf_test_case config2_userdata_write_files
|
||||
|
||||
@@ -1274,6 +1275,39 @@ EOF
|
||||
true
|
||||
}
|
||||
|
||||
config2_userdata_multipart_head()
|
||||
{
|
||||
atf_set "require.user" root
|
||||
}
|
||||
config2_userdata_multipart_body()
|
||||
{
|
||||
mkdir -p media/nuageinit
|
||||
setup_test_adduser
|
||||
printf "{}" > media/nuageinit/meta_data.json
|
||||
cat > media/nuageinit/user_data <<'EOF'
|
||||
Content-Type: multipart/mixed; boundary="==BOUNDARY=="
|
||||
|
||||
--==BOUNDARY==
|
||||
Content-Type: text/cloud-config; charset="us-ascii"
|
||||
|
||||
#cloud-config
|
||||
hostname: multipart-host
|
||||
|
||||
--==BOUNDARY==
|
||||
Content-Type: text/x-shellscript
|
||||
|
||||
#!/bin/sh
|
||||
echo "multipart script executed"
|
||||
|
||||
--==BOUNDARY==--
|
||||
EOF
|
||||
atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
|
||||
atf_check -o inline:"hostname=\"multipart-host\"\n" cat etc/rc.conf.d/hostname
|
||||
atf_check -o inline:"#!/bin/sh\necho \"multipart script executed\"\n" cat var/cache/nuageinit/multipart_script
|
||||
test -x var/cache/nuageinit/multipart_script || atf_fail "multipart_script not executable"
|
||||
true
|
||||
}
|
||||
|
||||
config2_userdata_fqdn_and_hostname_body()
|
||||
{
|
||||
mkdir -p media/nuageinit
|
||||
@@ -1329,6 +1363,7 @@ atf_init_test_cases()
|
||||
atf_add_test_case config2_userdata_ssh_authkey_fingerprints
|
||||
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_fqdn_and_hostname
|
||||
atf_add_test_case config2_userdata_write_files
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user