Add image name input to cloud-init build process and update handling
Enhance the dashboard UI by introducing an optional input field for the image name in the cloud-init build form. Update the API to process the image name, ensuring it is sanitized and included in the build request. Modify the build script to utilize the provided image name, allowing for customized output filenames during the image creation process. This improves user experience by offering more flexibility in naming cloud-init images.
This commit is contained in:
@@ -1636,12 +1636,15 @@ def api_build_cloudinit():
|
|||||||
meta_data = body.get("meta_data") or DEFAULT_META_DATA
|
meta_data = body.get("meta_data") or DEFAULT_META_DATA
|
||||||
network_config = body.get("network_config") or DEFAULT_NETWORK_CONFIG
|
network_config = body.get("network_config") or DEFAULT_NETWORK_CONFIG
|
||||||
set_as_golden_after = bool(body.get("set_as_golden_after"))
|
set_as_golden_after = bool(body.get("set_as_golden_after"))
|
||||||
|
image_name = (body.get("image_name") or "").strip()[:64]
|
||||||
|
image_name = re.sub(r"[^\w\-]", "", image_name) # only alphanumeric, underscore, dash
|
||||||
try:
|
try:
|
||||||
BUILD_REQUEST_FILE.parent.mkdir(parents=True, exist_ok=True)
|
BUILD_REQUEST_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with open(BUILD_REQUEST_FILE, "w") as f:
|
with open(BUILD_REQUEST_FILE, "w") as f:
|
||||||
json.dump({
|
json.dump({
|
||||||
"url": url,
|
"url": url,
|
||||||
"variant": variant,
|
"variant": variant,
|
||||||
|
"image_name": image_name or None,
|
||||||
"user_data": user_data,
|
"user_data": user_data,
|
||||||
"meta_data": meta_data,
|
"meta_data": meta_data,
|
||||||
"network_config": network_config,
|
"network_config": network_config,
|
||||||
|
|||||||
@@ -70,6 +70,11 @@
|
|||||||
<select id="buildVariant"><option value="desktop" selected>Desktop (recommended)</option><option value="lite">Lite</option><option value="full">Full</option></select>
|
<select id="buildVariant"><option value="desktop" selected>Desktop (recommended)</option><option value="lite">Lite</option><option value="full">Full</option></select>
|
||||||
<span id="buildRaspiosUrl" class="mono" style="margin-left:0.5rem;"></span>
|
<span id="buildRaspiosUrl" class="mono" style="margin-left:0.5rem;"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin-bottom:0.5rem;">
|
||||||
|
<label>Image name (optional):</label>
|
||||||
|
<input type="text" id="buildImageName" placeholder="e.g. reterminal-kiosk" style="width:12rem; margin-left:0.25rem;" />
|
||||||
|
<span class="mono" style="font-size:0.85rem; margin-left:0.25rem;">+ date suffix</span>
|
||||||
|
</div>
|
||||||
<div style="margin-bottom:0.5rem;"><label><input type="checkbox" id="buildSetGolden" /> Set as golden after build</label></div>
|
<div style="margin-bottom:0.5rem;"><label><input type="checkbox" id="buildSetGolden" /> Set as golden after build</label></div>
|
||||||
<div style="margin-bottom:0.75rem;"><button type="button" id="buildCloudInitBtn" class="btn btn-primary">Download & build</button></div>
|
<div style="margin-bottom:0.75rem;"><button type="button" id="buildCloudInitBtn" class="btn btn-primary">Download & build</button></div>
|
||||||
<div id="buildCloudInitStatus" class="mono" style="min-height:1.2em;"></div>
|
<div id="buildCloudInitStatus" class="mono" style="min-height:1.2em;"></div>
|
||||||
@@ -143,7 +148,9 @@
|
|||||||
function startBuild() {
|
function startBuild() {
|
||||||
var btn = document.getElementById('buildCloudInitBtn');
|
var btn = document.getElementById('buildCloudInitBtn');
|
||||||
if (btn) btn.disabled = true;
|
if (btn) btn.disabled = true;
|
||||||
|
var nameEl = document.getElementById('buildImageName');
|
||||||
var body = { variant: document.getElementById('buildVariant').value, set_as_golden_after: document.getElementById('buildSetGolden').checked };
|
var body = { variant: document.getElementById('buildVariant').value, set_as_golden_after: document.getElementById('buildSetGolden').checked };
|
||||||
|
if (nameEl && nameEl.value.trim()) body.image_name = nameEl.value.trim();
|
||||||
body.user_data = document.getElementById('buildUserData').value.trim();
|
body.user_data = document.getElementById('buildUserData').value.trim();
|
||||||
body.meta_data = document.getElementById('buildMetaData').value.trim();
|
body.meta_data = document.getElementById('buildMetaData').value.trim();
|
||||||
body.network_config = document.getElementById('buildNetworkConfig').value.trim();
|
body.network_config = document.getElementById('buildNetworkConfig').value.trim();
|
||||||
|
|||||||
@@ -437,6 +437,11 @@
|
|||||||
</select>
|
</select>
|
||||||
<span id="buildRaspiosUrl" class="backups-mono" style="font-size:0.8rem; margin-left:0.5rem;"></span>
|
<span id="buildRaspiosUrl" class="backups-mono" style="font-size:0.8rem; margin-left:0.5rem;"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin-bottom:0.5rem;">
|
||||||
|
<label>Image name (optional): </label>
|
||||||
|
<input type="text" id="buildImageName" placeholder="e.g. reterminal-kiosk" style="width:12rem;" />
|
||||||
|
<span class="backups-mono" style="font-size:0.8rem; margin-left:0.5rem;">+ date suffix (e.g. 20251204-143022)</span>
|
||||||
|
</div>
|
||||||
<div style="margin-bottom:0.5rem;">
|
<div style="margin-bottom:0.5rem;">
|
||||||
<label><input type="checkbox" id="buildSetGolden" /> Set as golden image after build</label>
|
<label><input type="checkbox" id="buildSetGolden" /> Set as golden image after build</label>
|
||||||
<span class="backups-mono" style="font-size:0.8rem;"> (use for Deploy without clicking manually)</span>
|
<span class="backups-mono" style="font-size:0.8rem;"> (use for Deploy without clicking manually)</span>
|
||||||
@@ -913,9 +918,11 @@
|
|||||||
var md = document.getElementById('buildMetaData');
|
var md = document.getElementById('buildMetaData');
|
||||||
var nc = document.getElementById('buildNetworkConfig');
|
var nc = document.getElementById('buildNetworkConfig');
|
||||||
var setGolden = document.getElementById('buildSetGolden');
|
var setGolden = document.getElementById('buildSetGolden');
|
||||||
|
var nameEl = document.getElementById('buildImageName');
|
||||||
var body = {
|
var body = {
|
||||||
variant: getBuildVariant(),
|
variant: getBuildVariant(),
|
||||||
set_as_golden_after: setGolden && setGolden.checked,
|
set_as_golden_after: setGolden && setGolden.checked,
|
||||||
|
image_name: (nameEl && nameEl.value.trim()) ? nameEl.value.trim() : undefined,
|
||||||
user_data: (ud && ud.value.trim()) ? ud.value.trim() : undefined,
|
user_data: (ud && ud.value.trim()) ? ud.value.trim() : undefined,
|
||||||
meta_data: (md && md.value.trim()) ? md.value.trim() : undefined,
|
meta_data: (md && md.value.trim()) ? md.value.trim() : undefined,
|
||||||
network_config: (nc && nc.value.trim()) ? nc.value.trim() : undefined
|
network_config: (nc && nc.value.trim()) ? nc.value.trim() : undefined
|
||||||
|
|||||||
@@ -47,14 +47,24 @@ with open(f"{out}/url", "w") as f:
|
|||||||
f.write(d.get("url", ""))
|
f.write(d.get("url", ""))
|
||||||
with open(f"{out}/set_golden", "w") as f:
|
with open(f"{out}/set_golden", "w") as f:
|
||||||
f.write("1" if d.get("set_as_golden_after") else "0")
|
f.write("1" if d.get("set_as_golden_after") else "0")
|
||||||
|
name = (d.get("image_name") or "").strip()[:64]
|
||||||
|
name = "".join(c for c in name if c.isalnum() or c in "_-")
|
||||||
|
with open(f"{out}/image_name", "w") as f:
|
||||||
|
f.write(name)
|
||||||
PY
|
PY
|
||||||
|
|
||||||
URL=$(cat "$TEMP_DIR/url")
|
URL=$(cat "$TEMP_DIR/url")
|
||||||
VARIANT=$(cat "$TEMP_DIR/variant")
|
VARIANT=$(cat "$TEMP_DIR/variant")
|
||||||
SET_AS_GOLDEN=$(cat "$TEMP_DIR/set_golden")
|
SET_AS_GOLDEN=$(cat "$TEMP_DIR/set_golden")
|
||||||
|
IMAGE_NAME="$(cat "$TEMP_DIR/image_name" 2>/dev/null || true)"
|
||||||
[[ -n "$URL" ]] || { write_status "error" "" "" "Missing url in request"; exit 1; }
|
[[ -n "$URL" ]] || { write_status "error" "" "" "Missing url in request"; exit 1; }
|
||||||
|
|
||||||
OUT_NAME="raspios-${VARIANT}-cloudinit-$(date +%Y%m%d-%H%M%S).img"
|
DATE_SUFFIX="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
if [[ -n "$IMAGE_NAME" ]]; then
|
||||||
|
OUT_NAME="${IMAGE_NAME}-${DATE_SUFFIX}.img"
|
||||||
|
else
|
||||||
|
OUT_NAME="raspios-${VARIANT}-cloudinit-${DATE_SUFFIX}.img"
|
||||||
|
fi
|
||||||
OUT_PATH="$OUTPUT_DIR/$OUT_NAME"
|
OUT_PATH="$OUTPUT_DIR/$OUT_NAME"
|
||||||
|
|
||||||
BASENAME=$(basename "$URL")
|
BASENAME=$(basename "$URL")
|
||||||
|
|||||||
Reference in New Issue
Block a user