mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-02 19:02:31 +00:00
fix(oci): a new volume starts as the image has it
A container volume is filled with what the image holds at that path, with its owner and permissions, as Docker does with a new volume. It was only done for the recipes that asked for it. Images that run as a user of their own and do not take PUID, such as Node-RED or Grafana, find their data directory writable and start.
This commit is contained in:
@@ -440,11 +440,13 @@ cleanup_volume_seed_staging() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
capture_volume_seeds() {
|
capture_volume_seeds() {
|
||||||
local seed_count=0 rootfs="/var/lib/lxc/${VMID}/rootfs"
|
local seed_count=0 volume_count=0 rootfs="/var/lib/lxc/${VMID}/rootfs"
|
||||||
local encoded item target source_path selected_mount_type mount_types source stage index=0 mounted=0 failed=0
|
local encoded item target source_path selected_mount_type mount_types source stage index=0 mounted=0 failed=0
|
||||||
|
local -A declared_seeds=()
|
||||||
|
|
||||||
seed_count=$(jq '.proxmox.installer_profile.volume_seeds? // [] | length' "$TEMPLATE_FILE")
|
seed_count=$(jq '.proxmox.installer_profile.volume_seeds? // [] | length' "$TEMPLATE_FILE")
|
||||||
(( seed_count > 0 )) || return 0
|
volume_count=$(jq '[.mounts[]? | select(.type == "managed-volume")] | length' "$DEPLOYMENT_FILE")
|
||||||
|
(( seed_count > 0 || volume_count > 0 )) || return 0
|
||||||
|
|
||||||
mount_ct_rootfs
|
mount_ct_rootfs
|
||||||
mounted=1
|
mounted=1
|
||||||
@@ -453,6 +455,7 @@ capture_volume_seeds() {
|
|||||||
[[ -n $encoded ]] || continue
|
[[ -n $encoded ]] || continue
|
||||||
item=$(printf '%s' "$encoded" | base64 -d)
|
item=$(printf '%s' "$encoded" | base64 -d)
|
||||||
target=$(jq -er '.container_path' <<<"$item")
|
target=$(jq -er '.container_path' <<<"$item")
|
||||||
|
declared_seeds[$target]=1
|
||||||
source_path=$(jq -er '.source_path' <<<"$item")
|
source_path=$(jq -er '.source_path' <<<"$item")
|
||||||
mount_types=$(jq -c '.mount_types // ["managed-volume"]' <<<"$item")
|
mount_types=$(jq -c '.mount_types // ["managed-volume"]' <<<"$item")
|
||||||
selected_mount_type=$(jq -r --arg target "$target" \
|
selected_mount_type=$(jq -r --arg target "$target" \
|
||||||
@@ -489,6 +492,24 @@ capture_volume_seeds() {
|
|||||||
index=$((index + 1))
|
index=$((index + 1))
|
||||||
done < <(jq -r '.proxmox.installer_profile.volume_seeds[]? | @base64' "$TEMPLATE_FILE")
|
done < <(jq -r '.proxmox.installer_profile.volume_seeds[]? | @base64' "$TEMPLATE_FILE")
|
||||||
|
|
||||||
|
# Docker fills a new volume with what the image has at that path, with its
|
||||||
|
# owner and permissions. Every new container volume gets the same here, so
|
||||||
|
# an image that runs as a user of its own finds its data directory its own.
|
||||||
|
while (( failed == 0 )) && IFS= read -r target; do
|
||||||
|
[[ -n $target && -z ${declared_seeds[$target]:-} ]] || continue
|
||||||
|
[[ $target == /* && $target != *[[:space:]]* && $target != *","* ]] || continue
|
||||||
|
is_reused_managed_mount "$target" && continue
|
||||||
|
[[ -d "${rootfs}${target}" && ! -L "${rootfs}${target}" ]] || continue
|
||||||
|
source=$(readlink -e -- "${rootfs}${target}") || continue
|
||||||
|
[[ $source == "${rootfs}"/* ]] || continue
|
||||||
|
stage="${VOLUME_SEED_STAGE_ROOT}/${index}"
|
||||||
|
install -d -m 0700 "$stage" && cp -a -- "${source}/." "${stage}/" || { failed=1; break; }
|
||||||
|
VOLUME_SEED_TARGETS+=("$target")
|
||||||
|
VOLUME_SEED_STAGES+=("$stage")
|
||||||
|
VOLUME_SEED_MOUNT_TYPES+=("managed-volume")
|
||||||
|
index=$((index + 1))
|
||||||
|
done < <(jq -r '.mounts[]? | select(.type == "managed-volume") | .container_path' "$DEPLOYMENT_FILE")
|
||||||
|
|
||||||
if (( mounted == 1 )); then
|
if (( mounted == 1 )); then
|
||||||
oci_quiet pct unmount "$VMID" || failed=1
|
oci_quiet pct unmount "$VMID" || failed=1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -71,6 +71,16 @@ class ContentVolumeOwnerTests(unittest.TestCase):
|
|||||||
self.assertNotIn("/etc/timezone", paths, app)
|
self.assertNotIn("/etc/timezone", paths, app)
|
||||||
self.assertNotIn("/etc/localtime", owners(template), app)
|
self.assertNotIn("/etc/localtime", owners(template), app)
|
||||||
|
|
||||||
|
def test_a_new_volume_is_filled_from_the_image_as_docker_does(self):
|
||||||
|
# Declared or not: an image that runs as a user of its own, as Node-RED
|
||||||
|
# or Grafana, finds its data directory with its own owner.
|
||||||
|
installer = (ROOT / "remote" / "install_oci.sh").read_text()
|
||||||
|
capture = installer[installer.index("capture_volume_seeds() {"):installer.index("apply_volume_seeds() {")]
|
||||||
|
self.assertIn('select(.type == "managed-volume") | .container_path\' "$DEPLOYMENT_FILE")', capture)
|
||||||
|
self.assertIn('is_reused_managed_mount "$target" && continue', capture)
|
||||||
|
self.assertIn('cp -a -- "${source}/." "${stage}/"', capture)
|
||||||
|
self.assertIn("[[ -n $target && -z ${declared_seeds[$target]:-} ]] || continue", capture)
|
||||||
|
|
||||||
def test_a_recipe_without_preparations_is_left_alone(self):
|
def test_a_recipe_without_preparations_is_left_alone(self):
|
||||||
contract = {"volumes": [{"container_path": "/downloads", "installation_choice": ["managed-volume", "host-bind"]}]}
|
contract = {"volumes": [{"container_path": "/downloads", "installation_choice": ["managed-volume", "host-bind"]}]}
|
||||||
self.catalog._apply_volume_policy("an-application", contract, None)
|
self.catalog._apply_volume_policy("an-application", contract, None)
|
||||||
|
|||||||
Reference in New Issue
Block a user