fix: OCI image data paths, LAN endpoints for OpenAI-compatible AI and lab-validated apps

This commit is contained in:
MacRimi
2026-11-02 18:42:32 +01:00
parent 9f3fffd249
commit b0215d4bbb
33 changed files with 566 additions and 39 deletions
+9 -1
View File
@@ -2,7 +2,7 @@
<!-- Generated from oci/catalog/verification.json by .github/scripts/oci_validation.py. Do not edit by hand. -->
44 of 532 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 3 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
52 of 532 applications in the OCI catalog have been tested for real: 49 in the ProxMenux lab and 3 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
Each test describes the scenario that was run and names the image it ran on. It does not cover every possible configuration of the application, and a newer image has not been tested until someone reports it.
@@ -38,6 +38,7 @@ Questions and ideas about OCI testing go in [discussion #440](https://github.com
| Alby Hub ✨ | Finance & Budgeting |
| Alist | Files & Sharing |
| aMule | Files & Downloads |
| Authentik | Authentication & Security |
| Chromium | Browsers & Web Desktops |
| CodeProject.AI Server | AI |
| CopyParty | Files & Sharing |
@@ -50,26 +51,33 @@ Questions and ideas about OCI testing go in [discussion #440](https://github.com
| Flexget | *Arr Suite |
| Frigate | NVR & Cameras |
| Grafana | Monitoring & Analytics |
| HAOS One | IoT & Smart Home |
| Immich | Media |
| JDownloader | Files & Downloads |
| Jenkins CI/CD | AI / Coding & Dev-Tools |
| Linkwarden | Documents & Notes |
| Mariadb | Databases |
| Memos | Documents & Notes |
| MineOS | Gaming & Leisure |
| Motioneye | NVR & Cameras |
| Navidrome | Media & Streaming |
| Nextcloud | Productivity & Workflows |
| Nextcloud Stack | Productivity & Workflows |
| OpenList | Files & Sharing |
| Openssh Server | Remote Access & VPN |
| Paperless-ngx | Productivity & Workflows |
| Phpmyadmin | Databases |
| Prowlarr | *Arr Suite |
| Qbittorrent | Files & Downloads |
| Rclone WebUI | Backup & Recovery |
| Real-Debrid Torrent Client | Files & Downloads |
| Scanopy | Network & Firewall |
| ShareCoveX | Files & Sharing |
| SnapOtter | Media & Streaming |
| Sonarr | *Arr Suite |
| Tandoor Recipes | Productivity & Workflows |
| Thelounge | Communication & Community |
| Trilium | Documents & Notes |
| Vaultwarden | Authentication & Security |
| Wireguard | Remote Access & VPN |
| WireGuard Easy | Remote Access & VPN |
@@ -323,6 +323,9 @@
"AceStream HTTP Proxy has no web interface: a media player opens its streams.",
"HLS stream: port 6878, path /ace/manifest.m3u8?id= followed by the channel ID.",
"MPEG-TS stream: port 6878, path /ace/getstream?id= followed by the channel ID."
],
"non_persistent_image_volumes": [
"/home/appuser/.ACEStream"
]
}
},
+62 -2
View File
@@ -76,6 +76,38 @@
"backup": true,
"default_size_gb": 2
}
},
{
"id": "volume-1",
"container_path": "/etc/bind",
"compose_source_example": "./config",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
},
{
"id": "volume-2",
"container_path": "/var/lib/bind",
"compose_source_example": "./lib",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
@@ -123,7 +155,9 @@
"53:53/tcp"
],
"volumes": [
"./bind:/var/cache/bind"
"./bind:/var/cache/bind",
"./config:/etc/bind",
"./lib:/var/lib/bind"
]
}
}
@@ -151,6 +185,28 @@
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "bind9-volume-1",
"service": "bind9",
"container_path": "/etc/bind",
"mode": "managed-volume",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "bind9-volume-2",
"service": "bind9",
"container_path": "/var/lib/bind",
"mode": "managed-volume",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
@@ -349,7 +405,11 @@
"validation": "not-requested-by-compose"
}
],
"installer_profile": {},
"installer_profile": {
"non_persistent_image_volumes": [
"/var/log"
]
},
"catalog": {
"replaces_discovered_ids": []
}
+7 -1
View File
@@ -339,7 +339,13 @@
"validation": "not-requested-by-compose"
}
],
"installer_profile": {}
"installer_profile": {
"non_persistent_image_volumes": [
"/etc/chrony",
"/run/chrony",
"/var/lib/chrony"
]
}
},
"compatibility": {
"automatic_install_candidate": true,
+4 -1
View File
@@ -409,7 +409,10 @@
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": true
}
},
"non_persistent_image_volumes": [
"/gotosocial/.cache"
]
}
},
"compatibility": {
+5 -1
View File
@@ -345,7 +345,11 @@
"validation": "not-requested-by-compose"
}
],
"installer_profile": {},
"installer_profile": {
"non_persistent_image_volumes": [
"/tmp"
]
},
"catalog": {
"replaces_discovered_ids": []
}
+35 -1
View File
@@ -94,6 +94,22 @@
"backup": true,
"default_size_gb": 8
}
},
{
"id": "volume-2",
"container_path": "/etc/nginx/ssl",
"compose_source_example": "./ssl",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
@@ -134,7 +150,8 @@
],
"volumes": [
"./data:/var/www/app/data",
"./plugins:/var/www/app/plugins"
"./plugins:/var/www/app/plugins",
"./ssl:/etc/nginx/ssl"
]
}
}
@@ -171,6 +188,17 @@
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "kanboard-volume-2",
"service": "kanboard",
"container_path": "/etc/nginx/ssl",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
@@ -391,6 +419,12 @@
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/etc/nginx/ssl",
"remove_lost_found": true,
"owner_strategy": "mapped-root",
"only_when_mount_type": "managed-volume"
}
],
"startup_healthcheck": {
+4 -1
View File
@@ -329,7 +329,10 @@
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": true
}
},
"non_persistent_image_volumes": [
"/tmp/jetty"
]
}
},
"compatibility": {
+3
View File
@@ -788,6 +788,9 @@
"owner_strategy": "mapped-root",
"only_when_mount_type": "managed-volume"
}
],
"non_persistent_image_volumes": [
"/romm"
]
}
},
+47 -3
View File
@@ -60,7 +60,24 @@
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [],
"volumes": [],
"volumes": [
{
"id": "volume-0",
"container_path": "/var/lib/tor",
"compose_source_example": "./tor-data",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
{
"container_port": 9050,
@@ -103,6 +120,9 @@
"ports": [
"9050:9050",
"8118:8118"
],
"volumes": [
"./tor-data:/var/lib/tor"
]
}
}
@@ -119,7 +139,19 @@
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [],
"storage": [
{
"id": "tor-volume-0",
"service": "tor",
"container_path": "/var/lib/tor",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
@@ -323,7 +355,19 @@
"SOCKS5 proxy: configure your application with the LXC IP address shown above and port 9050.",
"HTTP CONNECT proxy: use the same IP address and port 8118. These addresses are proxy settings, not web links.",
"Only applications configured to use this proxy send their traffic through Tor."
]
],
"volume_preparations": [
{
"container_path": "/var/lib/tor",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
],
"volume_owner": {
"uid": 101,
"gid": 102
}
}
},
"compatibility": {
+4 -1
View File
@@ -323,7 +323,10 @@
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": true
}
},
"non_persistent_image_volumes": [
"/var/log"
]
},
"adaptations": [
{
+47 -3
View File
@@ -77,7 +77,24 @@
"source": "upstream-documentation"
}
],
"volumes": [],
"volumes": [
{
"id": "volume-0",
"container_path": "/config",
"compose_source_example": "./config",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
{
"container_port": 5000,
@@ -117,7 +134,10 @@
"environment": {
"WHOOGLE_USER": "admin",
"WHOOGLE_PASS": "${GENERATED_WHOOGLE_PASS}"
}
},
"volumes": [
"./config:/config"
]
}
}
],
@@ -133,7 +153,19 @@
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [],
"storage": [
{
"id": "whoogle-volume-0",
"service": "whoogle",
"container_path": "/config",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
@@ -378,6 +410,18 @@
"bytes": 24,
"prompt": false
}
},
"volume_preparations": [
{
"container_path": "/config",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
],
"volume_owner": {
"uid": 927,
"gid": 927
}
},
"catalog": {
@@ -323,6 +323,9 @@
"AceStream HTTP Proxy has no web interface: a media player opens its streams.",
"HLS stream: port 6878, path /ace/manifest.m3u8?id= followed by the channel ID.",
"MPEG-TS stream: port 6878, path /ace/getstream?id= followed by the channel ID."
],
"non_persistent_image_volumes": [
"/home/appuser/.ACEStream"
]
}
},
+62 -2
View File
@@ -76,6 +76,38 @@
"backup": true,
"default_size_gb": 2
}
},
{
"id": "volume-1",
"container_path": "/etc/bind",
"compose_source_example": "./config",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
},
{
"id": "volume-2",
"container_path": "/var/lib/bind",
"compose_source_example": "./lib",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
@@ -123,7 +155,9 @@
"53:53/tcp"
],
"volumes": [
"./bind:/var/cache/bind"
"./bind:/var/cache/bind",
"./config:/etc/bind",
"./lib:/var/lib/bind"
]
}
}
@@ -151,6 +185,28 @@
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "bind9-volume-1",
"service": "bind9",
"container_path": "/etc/bind",
"mode": "managed-volume",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "bind9-volume-2",
"service": "bind9",
"container_path": "/var/lib/bind",
"mode": "managed-volume",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
@@ -349,7 +405,11 @@
"validation": "not-requested-by-compose"
}
],
"installer_profile": {},
"installer_profile": {
"non_persistent_image_volumes": [
"/var/log"
]
},
"catalog": {
"replaces_discovered_ids": []
}
+7 -1
View File
@@ -339,7 +339,13 @@
"validation": "not-requested-by-compose"
}
],
"installer_profile": {}
"installer_profile": {
"non_persistent_image_volumes": [
"/etc/chrony",
"/run/chrony",
"/var/lib/chrony"
]
}
},
"compatibility": {
"automatic_install_candidate": true,
+4 -1
View File
@@ -409,7 +409,10 @@
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": true
}
},
"non_persistent_image_volumes": [
"/gotosocial/.cache"
]
}
},
"compatibility": {
+5 -1
View File
@@ -345,7 +345,11 @@
"validation": "not-requested-by-compose"
}
],
"installer_profile": {},
"installer_profile": {
"non_persistent_image_volumes": [
"/tmp"
]
},
"catalog": {
"replaces_discovered_ids": []
}
+35 -1
View File
@@ -94,6 +94,22 @@
"backup": true,
"default_size_gb": 8
}
},
{
"id": "volume-2",
"container_path": "/etc/nginx/ssl",
"compose_source_example": "./ssl",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
@@ -134,7 +150,8 @@
],
"volumes": [
"./data:/var/www/app/data",
"./plugins:/var/www/app/plugins"
"./plugins:/var/www/app/plugins",
"./ssl:/etc/nginx/ssl"
]
}
}
@@ -171,6 +188,17 @@
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "kanboard-volume-2",
"service": "kanboard",
"container_path": "/etc/nginx/ssl",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
@@ -391,6 +419,12 @@
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/etc/nginx/ssl",
"remove_lost_found": true,
"owner_strategy": "mapped-root",
"only_when_mount_type": "managed-volume"
}
],
"startup_healthcheck": {
+4 -1
View File
@@ -329,7 +329,10 @@
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": true
}
},
"non_persistent_image_volumes": [
"/tmp/jetty"
]
}
},
"compatibility": {
+47 -3
View File
@@ -60,7 +60,24 @@
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [],
"volumes": [],
"volumes": [
{
"id": "volume-0",
"container_path": "/var/lib/tor",
"compose_source_example": "./tor-data",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
{
"container_port": 9050,
@@ -103,6 +120,9 @@
"ports": [
"9050:9050",
"8118:8118"
],
"volumes": [
"./tor-data:/var/lib/tor"
]
}
}
@@ -119,7 +139,19 @@
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [],
"storage": [
{
"id": "tor-volume-0",
"service": "tor",
"container_path": "/var/lib/tor",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
@@ -323,7 +355,19 @@
"SOCKS5 proxy: configure your application with the LXC IP address shown above and port 9050.",
"HTTP CONNECT proxy: use the same IP address and port 8118. These addresses are proxy settings, not web links.",
"Only applications configured to use this proxy send their traffic through Tor."
]
],
"volume_preparations": [
{
"container_path": "/var/lib/tor",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
],
"volume_owner": {
"uid": 101,
"gid": 102
}
}
},
"compatibility": {
+4 -1
View File
@@ -323,7 +323,10 @@
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": true
}
},
"non_persistent_image_volumes": [
"/var/log"
]
},
"adaptations": [
{
+47 -3
View File
@@ -77,7 +77,24 @@
"source": "upstream-documentation"
}
],
"volumes": [],
"volumes": [
{
"id": "volume-0",
"container_path": "/config",
"compose_source_example": "./config",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
{
"container_port": 5000,
@@ -117,7 +134,10 @@
"environment": {
"WHOOGLE_USER": "admin",
"WHOOGLE_PASS": "${GENERATED_WHOOGLE_PASS}"
}
},
"volumes": [
"./config:/config"
]
}
}
],
@@ -133,7 +153,19 @@
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [],
"storage": [
{
"id": "whoogle-volume-0",
"service": "whoogle",
"container_path": "/config",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
@@ -378,6 +410,18 @@
"bytes": 24,
"prompt": false
}
},
"volume_preparations": [
{
"container_path": "/config",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
],
"volume_owner": {
"uid": 927,
"gid": 927
}
},
"catalog": {
+3
View File
@@ -99,6 +99,9 @@
"owner_strategy": "mapped-root",
"only_when_mount_type": "managed-volume"
}
],
"non_persistent_image_volumes": [
"/romm"
]
}
}
+24
View File
@@ -19,6 +19,9 @@
"amule": {
"status": "laboratory-validated"
},
"authentik": {
"status": "laboratory-validated"
},
"chromium": {
"status": "laboratory-validated"
},
@@ -62,6 +65,9 @@
"grafana": {
"status": "laboratory-validated"
},
"haos-one": {
"status": "laboratory-validated"
},
"immich": {
"status": "laboratory-validated"
},
@@ -74,6 +80,9 @@
"linkwarden": {
"status": "laboratory-validated"
},
"mariadb": {
"status": "laboratory-validated"
},
"memos": {
"status": "laboratory-validated"
},
@@ -83,6 +92,9 @@
"motioneye": {
"status": "laboratory-validated"
},
"navidrome": {
"status": "laboratory-validated"
},
"nextcloud": {
"status": "laboratory-validated"
},
@@ -108,6 +120,9 @@
"report": "https://github.com/MacRimi/ProxMenux/discussions/392#discussioncomment-18623868"
}
},
"prowlarr": {
"status": "laboratory-validated"
},
"qbittorrent": {
"status": "laboratory-validated"
},
@@ -117,12 +132,18 @@
"rdtclient": {
"status": "laboratory-validated"
},
"scanopy": {
"status": "laboratory-validated"
},
"sharecovex": {
"status": "laboratory-validated"
},
"snapotter": {
"status": "laboratory-validated"
},
"sonarr": {
"status": "laboratory-validated"
},
"tandoor": {
"status": "laboratory-validated"
},
@@ -140,6 +161,9 @@
"trilium": {
"status": "laboratory-validated"
},
"vaultwarden": {
"status": "laboratory-validated"
},
"wg-easy": {
"status": "laboratory-validated"
},
+3 -2
View File
@@ -728,8 +728,9 @@ def preflight(record, candidate, config, coordinated=None, in_place=False, basel
raise ValueError(translate('Required persistent paths cannot be removed'))
image_paths = set(record['observed']['image']['defaults'].get('Volumes') or {})
image_paths -= non_persistent_image_volumes(record['template'])
if any(not any(p == target or p.startswith(target.rstrip('/') + '/') for target in old) for p in image_paths):
raise ValueError(translate('The image declares data paths that are still stored in the rootfs'))
in_rootfs = sorted(p for p in image_paths if not any(p == target or p.startswith(target.rstrip('/') + '/') for target in old))
if in_rootfs:
raise ValueError(f"{translate('The image declares data paths that are still stored in the rootfs')}: {', '.join(in_rootfs)}")
check = effective_healthcheck(candidate['template'])
if not check and not coordinated:
raise ValueError(translate('Updates are not available yet for this application in this beta'))
+9 -3
View File
@@ -219,6 +219,11 @@ def service_template(parent, service, main=False):
own_preparations.append(entry)
if own_preparations:
single['proxmox'].setdefault('installer_profile', {})['volume_preparations'] = own_preparations
# Image paths the oci-compose states hold no data; an image that does not
# declare one of them is not affected by it.
transient = parent.get('proxmox', {}).get('installer_profile', {}).get('non_persistent_image_volumes')
if transient:
single['proxmox'].setdefault('installer_profile', {})['non_persistent_image_volumes'] = list(transient)
return single
@@ -784,10 +789,11 @@ def apply_stack_support(template):
template['status'] = 'generated-review-required'
return
template['proxmox'].pop('generic_stack_review',None)
preserved_preparations = template['proxmox'].get('installer_profile',{}).get('volume_preparations')
profile = template['proxmox'].get('installer_profile',{})
template['proxmox']['installer_profile'] = {'stack_driver':'generic-multi-lxc-stack'}
if preserved_preparations:
template['proxmox']['installer_profile']['volume_preparations'] = preserved_preparations
for key in ('volume_preparations', 'non_persistent_image_volumes'):
if profile.get(key):
template['proxmox']['installer_profile'][key] = profile[key]
template['compatibility']['untranslated_blockers'] = []
template['compatibility']['automatic_install_candidate'] = True
template['status'] = 'generated-unvalidated'
@@ -0,0 +1,30 @@
"""Image paths a stack's oci-compose states hold no data reach the template
of every container of the stack, where updates and Modify read them."""
import copy
from pathlib import Path
import sys
import unittest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from proxmenux_oci.catalog import Catalog
from proxmenux_oci.stack import apply_stack_support, build_stack, DefaultsUI
class StackTransientImageVolumesTests(unittest.TestCase):
def test_each_container_of_romm_receives_the_transient_paths(self):
result = build_stack(Catalog(ROOT).compose("romm"), DefaultsUI(), mode="simple")
for service in result["services"]:
profile = service["template"]["proxmox"]["installer_profile"]
self.assertEqual(profile.get("non_persistent_image_volumes"), ["/romm"], service["name"])
def test_the_stack_profile_keeps_them_when_it_is_rewritten(self):
template = copy.deepcopy(Catalog(ROOT).compose("romm"))
template["proxmox"]["installer_profile"]["stack_driver"] = "generic-multi-lxc-stack"
apply_stack_support(template)
self.assertEqual(template["proxmox"]["installer_profile"].get("non_persistent_image_volumes"), ["/romm"])
if __name__ == "__main__":
unittest.main()
+3 -1
View File
@@ -164,7 +164,9 @@ class UpdateCheckTests(unittest.TestCase):
check.check(self.root, self.configs, path=path, progress=lambda *event: events.append(event))
native.assert_called_once(); notify.assert_not_called(); external.assert_not_called()
self.assertEqual(events[0], (0, 1)); self.assertEqual(events[-1], (1, 1))
self.assertEqual([r['vmid'] for r in self.status()['available']], [110])
# The bridge stamps its result with the real clock, so read it at the real time too.
available = check.cached(self.root, self.configs, apps=self.apps)['available']
self.assertEqual([r['vmid'] for r in available], [110])
self.assertFalse((self.root / '.update-check.json').exists())
def test_missing_monitor_never_falls_back_to_registry_commands(self):