fix(oci): the access prompt says what it grants, and two checks name what differs

This commit is contained in:
MacRimi
2026-10-09 00:43:32 +02:00
parent 316eb2f104
commit 16ab9808ac
6 changed files with 26 additions and 16 deletions
+6 -4
View File
@@ -189,7 +189,7 @@
"Access bridge for Nextcloud": "Puente de acceso para Nextcloud",
"Access bridge for Paperless": "Puente de acceso para Paperless",
"Access bridge for Tandoor": "Puente de acceso para Tandoor",
"Access could not be granted; the application cannot write to:": "No se pudo conceder el acceso; la aplicación no puede escribir en:",
"Access could not be granted completely; the application may be unable to write to:": "No se pudo conceder el acceso por completo; puede que la aplicación no pueda escribir en:",
"Access granted to the application on:": "Acceso concedido a la aplicación en:",
"Access profile:": "Perfil de acceso:",
"Access token of the Jupyter Lab web interface": "Token de acceso de la interfaz web de Jupyter Lab",
@@ -6175,7 +6175,7 @@
"The container devices do not match the saved record": "Los dispositivos del contenedor no coinciden con el registro guardado",
"The container did not stop to update its persistent configuration:": "El contenedor no se detuvo para actualizar su configuración persistente:",
"The container did not stop; its disks are not touched": "El contenedor no se detuvo; sus discos no se tocan",
"The container disks do not match the saved record": "Los discos de contenedores no coinciden con el disco guardado",
"The container disks do not match the saved record": "Los discos del contenedor no coinciden con el registro guardado",
"The container does not exist:": "El contenedor no existe:",
"The container does not have the fuse=1 feature enabled": "El contenedor no tiene activada la función fuse=1",
"The container does not need it any more; an update downloads the new version when there is one.": "El contenedor ya no lo necesita; una actualización descarga la nueva versión cuando la hay.",
@@ -7885,7 +7885,7 @@
"zfs-auto-snapshot removed (existing snapshots preserved on the pool)": "zfs-auto-snapshot eliminado (las instantáneas existentes se conservan en el grupo)",
"zpool command not found. Install zfsutils-linux and retry.": "Comando zpool no encontrado. Instale zfsutils-linux y vuelva a intentarlo.",
"zpool not found. Install zfsutils-linux and retry.": "zpool no encontrado. Instale zfsutils-linux y vuelva a intentarlo.",
"{path} already exists and belongs to another user of the host, so {app} cannot write to it.\n\nProxMenux can add an access rule (ACL) for the identity {app} uses on the host (UID {uid}). Nothing that is there changes owner or permissions: whatever reads or writes those files today keeps doing so, and the owner of the directory also gets access to the files {app} creates.\n\nGrant {app} access to this directory?": "{path} ya existe y pertenece a otro usuario del host, así que {app} no puede escribir en él.\n\nProxMenux puede añadir una regla de acceso (ACL) para la identidad que {app} usa en el host (UID {uid}). Nada de lo que hay cambia de propietario ni de permisos: lo que hoy lee o escribe esos archivos lo sigue haciendo, y el propietario del directorio también tiene acceso a los archivos que cree {app}.\n\n¿Dar acceso a {app} a este directorio?",
"{path} already exists and belongs to another user of the host, so {app} cannot write to it.\n\nProxMenux can add access rules (ACL) to this directory, to everything inside it and to what is created there later, for the users and groups {app} runs as on the host (IDs {ids}). The owner of the files does not change and whatever reads or writes them today keeps doing so; the owner of the directory also gets access to the files {app} creates.\n\nGrant {app} access to this directory?": "{path} ya existe y pertenece a otro usuario del host, así que {app} no puede escribir en él.\n\nProxMenux puede añadir reglas de acceso (ACL) a este directorio, a todo lo que contiene y a lo que se cree en él después, para los usuarios y grupos con los que {app} se ejecuta en el host (ID {ids}). El propietario de los archivos no cambia y lo que hoy los lee o escribe lo sigue haciendo; el propietario del directorio también tiene acceso a los archivos que cree {app}.\n\n¿Dar acceso a {app} a este directorio?",
"— Full/Custom restore will skip kernel-tied paths": "— La restauración completa/personalizada omitirá las rutas vinculadas al kernel",
"— disk may be busy. Skipping fstab removal.": "— el disco puede estar ocupado. Saltarse la eliminación de fstab.",
"— restore proceeds as same-version (verified safe)": "- la restauración procede como la misma versión (segura verificada)",
@@ -7917,5 +7917,7 @@
"⚠ Disk data will NOT be erased.": "⚠ Los datos del disco NO se borrarán.",
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ El disco se desmontará y se eliminará de /etc/fstab.",
"⚠ The /etc/fstab entry will be removed.": "⚠ Se eliminará la entrada /etc/fstab.",
"⚠ The disk will be unmounted.": "⚠ El disco se desmontará."
"⚠ The disk will be unmounted.": "⚠ El disco se desmontará.",
"The privilege mode of the container does not match the saved record": "El modo de privilegios del contenedor no coincide con el registro guardado",
"The host monitor include is unknown or differs from the saved record": "El include del monitor del host es desconocido o difiere del registro guardado"
}
+1 -1
View File
@@ -1876,7 +1876,7 @@ while IFS=$'\t' read -r TYPE TARGET SOURCE SIZE BACKUP READ_ONLY CREATE_IF_MISSI
MOUNT_NOTES+=("$(translate "Access granted to the application on:") $SOURCE")
else
oci_log "Could not grant access with an ACL: $SOURCE"
MOUNT_WARNINGS+=("$(translate "Access could not be granted; the application cannot write to:") $SOURCE")
MOUNT_WARNINGS+=("$(translate "Access could not be granted completely; the application may be unable to write to:") $SOURCE")
fi
fi
if [[ -d $SOURCE ]]; then
+3 -1
View File
@@ -520,7 +520,9 @@ def preflight(record, candidate, config, coordinated=None):
else:
raise ValueError(translate('Unsupported mount type'))
paths.append(target)
if cfg.get('unprivileged', '0') != ('1' if unprivileged else '0') or ':' not in cfg.get('rootfs', ''):
if cfg.get('unprivileged', '0') != ('1' if unprivileged else '0'):
raise ValueError(translate('The privilege mode of the container does not match the saved record'))
if ':' not in cfg.get('rootfs', ''):
raise ValueError(translate('The container disks do not match the saved record'))
if desired['rootfs'] != deployment['rootfs']:
raise ValueError(translate('Changing the rootfs or its storage requires a separate migration'))
+1 -1
View File
@@ -92,7 +92,7 @@ def check(config, deployment, vmid):
includes = [line.split(': ',1)[1] for line in lines if line.startswith('lxc.include: ')]
if deployment.get('host_monitor'):
if not host_monitor_include(includes):
raise ValueError(translate('The sysctl include is unknown or differs from the saved record'))
raise ValueError(translate('The host monitor include is unknown or differs from the saved record'))
return
content = sysctl_content(deployment)
allowed = [[str(include_path(vmid))], [str(legacy_include_path(vmid))]] if content else [[]]
+9 -5
View File
@@ -194,13 +194,17 @@ def ask_shared_directories(ui, template: dict[str, Any], mounts: list[dict[str,
uid, gid = int(owner.get("uid", 0)) + shift, int(owner.get("gid", 0)) + shift
if os.path.isdir(mount["source"]) and not host.can_write(mount["source"], uid, gid):
title = source_text(template["catalog_ui"]["title"]) or app_id
# The rules name the identity of the application and, in an
# unprivileged container, the one its root has on the host.
ids = ", ".join(str(value) for value in dict.fromkeys([uid, gid, *([shift] if shift else [])]))
if ui.confirm(translate(
"{path} already exists and belongs to another user of the host, so {app} cannot "
"write to it.\n\nProxMenux can add an access rule (ACL) for the identity {app} uses "
"on the host (UID {uid}). Nothing that is there changes owner or permissions: "
"whatever reads or writes those files today keeps doing so, and the owner of the "
"directory also gets access to the files {app} creates.\n\nGrant {app} access to "
"this directory?").format(path=mount["source"], app=title, uid=uid), True):
"write to it.\n\nProxMenux can add access rules (ACL) to this directory, to "
"everything inside it and to what is created there later, for the users and groups "
"{app} runs as on the host (IDs {ids}). The owner of the files does not change and "
"whatever reads or writes them today keeps doing so; the owner of the directory "
"also gets access to the files {app} creates.\n\nGrant {app} access to this "
"directory?").format(path=mount["source"], app=title, ids=ids), True):
mount["grant_access"] = True
from .custom_mounts import validate_mount
mount["container_path"] = validate_mount(mount, [*mounts, *added])
+6 -4
View File
@@ -145,8 +145,9 @@ class SharedDirectoriesTests(unittest.TestCase):
question = next(text for text in ui.asked if "Grant ShareCoveX access" in text)
# The identity is the one of the volumes, in the range of an unprivileged container.
check.assert_called_once_with(directory, 101000, 101000)
self.assertIn("UID 101000", question)
self.assertIn("changes owner or permissions", question)
# The identity and the root of the container, as the host sees them.
self.assertIn("(IDs 101000, 100000)", question)
self.assertIn("everything inside it and to what is created there later", question)
self.assertIs(plan["mounts"][1]["grant_access"], True)
_ui, _template, declined = self.build({**answers, question: [False]})
self.assertNotIn("grant_access", declined["mounts"][1])
@@ -162,9 +163,10 @@ class SharedDirectoriesTests(unittest.TestCase):
first = build_deployment(template, ui, ADVANCED_MODE)
prompt = next(text for text in ui.asked if nfsv4 in text)
check.reset_mock()
build_deployment(self.catalog.compose("sharecovex"), ScriptedUI({**answers, prompt: [True]}),
ADVANCED_MODE)
privileged = ScriptedUI({**answers, prompt: [True]})
build_deployment(self.catalog.compose("sharecovex"), privileged, ADVANCED_MODE)
check.assert_called_once_with(directory, 1000, 1000)
self.assertTrue(any("(IDs 1000)" in text for text in privileged.asked))
self.assertIsNotNone(first)
def test_a_directory_that_does_not_exist_yet_is_created_for_the_application(self, *_):