Merge pull request #590 from jcastro/feature/oci-nextcloud-mariadb

feat(oci): add LinuxServer Nextcloud with MariaDB and Redis
This commit is contained in:
MacRimi
2026-11-05 19:50:06 +01:00
committed by GitHub
18 changed files with 1828 additions and 6 deletions
+6
View File
@@ -1644,6 +1644,7 @@
"Create scheduled backup job": "Erstellen Sie einen geplanten Sicherungsauftrag",
"Create share directory:": "Freigabeverzeichnis erstellen:",
"Create shared directory:": "Freigegebenes Verzeichnis erstellen:",
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux erzeugt das Administratorpasswort und zeigt es am Ende der Installation an. Ändern Sie es nach der ersten Anmeldung.",
"Create this LXC in privileged mode?": "Erstellen Sie diese LXC im privilegierten Modus?",
"Create with create_user in the container console": "Erstellen Sie den Benutzer mit create_user in der Container-Konsole",
"Create, send and sign documents with a self-hosted web app.": "Erstelle, versende und signiere Dokumente mit einer selbst gehosteten Web-App.",
@@ -3904,6 +3905,7 @@
"Linux/Mac path:": "Linux/Mac-Pfad:",
"LinuxServer Jellyfin with optional GPU passthrough": "LinuxServer Jellyfin mit optionalem GPU-Passthrough",
"LinuxServer MariaDB requires a user, database and password": "LinuxServer MariaDB requires ein Benutzer, Datenbank und Passwort",
"LinuxServer Nextcloud with MariaDB and Redis": "LinuxServer Nextcloud mit MariaDB und Redis",
"List Available Disks": "Verfügbare Festplatten auflisten",
"List IOMMU group mapping": "IOMMU-Gruppenzuordnung auflisten",
"List NVMe devices": "NVMe-Geräte auflisten",
@@ -4484,6 +4486,7 @@
"Next Step Required": "Nächster Schritt erforderlich",
"Next Steps:": "Nächste Schritte:",
"Next step: stop that VM first, then run": "Nächster Schritt: Stoppen Sie zuerst die VM und führen Sie sie dann aus",
"Nextcloud Web UI": "Nextcloud-Weboberfläche",
"Nextcloud configuration cancelled": "Nextcloud-Konfiguration gestrichen",
"Nextcloud gives you access to all your files wherever you are.": "Nextcloud bietet Ihnen Zugriff auf alle Ihre Dateien, wo immer Sie sind.",
"Nextcloud volume size in GB": "Nextcloud Volumengröße in GB",
@@ -5605,6 +5608,7 @@
"Recreation prepared": "Erholung vorbereitet",
"Recursive DNS resolver.": "Rekursiver DNS-Resolver.",
"Recursive and authoritative DNS server.": "Rekursiver und autoritativer DNS-Server.",
"Redis locking is configured automatically. Existing Nextcloud configuration files are preserved.": "Die Dateisperre mit Redis wird automatisch eingerichtet. Vorhandene Nextcloud-Konfigurationsdateien bleiben erhalten.",
"Redmine": "Redmine",
"Refresh APT index and verify repositories:": "APT-Index aktualisieren und Repositorys überprüfen:",
"Refresh catalog": "Katalog aktualisieren",
@@ -7995,6 +7999,7 @@
"Use a privileged LXC for NFS server/client workflows.": "Verwenden Sie einen privilegierten LXC für NFS-Server-/Client-Workflows.",
"Use a privileged LXC for Samba client/server workflows.": "Verwenden Sie einen privilegierten LXC für Samba-Client/Server-Workflows.",
"Use a registry hostname, for example ghcr.io or registry.example.com:5000.": "Verwenden Sie einen Registrierungs-Hostnamen, zum Beispiel ghcr.io oder registry.example.com:5000.",
"Use a trusted HTTPS reverse proxy. Upgrade Nextcloud one major version at a time.": "Verwenden Sie einen HTTPS-Reverse-Proxy mit einem vertrauenswürdigen Zertifikat. Aktualisieren Sie Nextcloud jeweils auf die nächste Hauptversion, ohne eine zu überspringen.",
"Use an existing SSH private key file on this host": "Verwenden Sie eine vorhandene private SSH-Schlüsseldatei auf diesem Host",
"Use an existing keyfile": "Verwenden Sie eine vorhandene Schlüsseldatei",
"Use an image reference, for example ghcr.io/owner/app:latest.": "Verwenden Sie eine Bildreferenz, zum Beispiel ghcr.io/owner/app:latest.",
@@ -8008,6 +8013,7 @@
"Use existing filesystem": "Vorhandenes Dateisystem verwenden",
"Use existing: mount without formatting": "Vorhandenes verwenden: mounten ohne Formatierung",
"Use images only if the directory is on suitable storage.": "Verwenden Sie Bilder nur, wenn sich das Verzeichnis auf einem geeigneten Speicher befindet.",
"Use managed storage for /config and MariaDB. A host/NAS directory can be selected for /data and needs a separate backup.": "Verwenden Sie verwalteten Speicher für /config und MariaDB. Für /data können Sie ein Verzeichnis auf dem Host/NAS auswählen, das eine eigene Sicherung benötigt.",
"Use mp1/mp2/... for extra mount points.": "Verwenden Sie mp1/mp2/... für zusätzliche Mount-Punkte.",
"Use only when unprivileged containers cannot meet your needs": "Verwenden Sie es nur, wenn nicht privilegierte Container Ihre Anforderungen nicht erfüllen können",
"Use option 1 to add a local disk (as Proxmox storage and/or as a host mount).": "Verwenden Sie Option 1, um eine lokale Festplatte hinzuzufügen (als Proxmox-Speicher und/oder als Host-Mount).",
+6
View File
@@ -1642,6 +1642,7 @@
"Create scheduled backup job": "Crear trabajo de backup programado",
"Create share directory:": "Crear directorio compartido:",
"Create shared directory:": "Crear directorio compartido:",
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux genera la contraseña del administrador y la muestra al final de la instalación. Cámbiala después del primer inicio de sesión.",
"Create this LXC in privileged mode?": "¿Crear este LXC en modo privilegiado?",
"Create with create_user in the container console": "Crea el usuario con create_user desde la consola del contenedor",
"Create, send and sign documents with a self-hosted web app.": "Crea, envía y firma documentos con una aplicación web autoalojada.",
@@ -3899,6 +3900,7 @@
"Linux/Mac path:": "Ruta Linux/Mac:",
"LinuxServer Jellyfin with optional GPU passthrough": "Jellyfin de LinuxServer con passthrough de GPU opcional",
"LinuxServer MariaDB requires a user, database and password": "LinuxServer MariaDB requiere un usuario, base de datos y contraseña",
"LinuxServer Nextcloud with MariaDB and Redis": "Nextcloud de LinuxServer con MariaDB y Redis",
"List Available Disks": "Listar discos disponibles",
"List IOMMU group mapping": "Listar el mapeo del grupo IOMMU",
"List NVMe devices": "Listar dispositivos NVMe",
@@ -4479,6 +4481,7 @@
"Next Step Required": "Se requiere el siguiente paso",
"Next Steps:": "Próximos pasos:",
"Next step: stop that VM first, then run": "Siguiente paso: detén esa VM primero y luego ejecuta",
"Nextcloud Web UI": "Interfaz web de Nextcloud",
"Nextcloud configuration cancelled": "Configuración Nextcloud cancelada",
"Nextcloud gives you access to all your files wherever you are.": "Nextcloud te da acceso a todos tus archivos estés donde estés.",
"Nextcloud volume size in GB": "Tamaño del volumen de Nextcloud en GB",
@@ -5588,6 +5591,7 @@
"Recreating the container:": "Recreando el contenedor:",
"Recursive DNS resolver.": "Resolvedor DNS recursivo.",
"Recursive and authoritative DNS server.": "Servidor DNS recursivo y autoritativo.",
"Redis locking is configured automatically. Existing Nextcloud configuration files are preserved.": "El bloqueo con Redis se configura automáticamente. Se conservan los archivos de configuración existentes de Nextcloud.",
"Redmine": "Redmine",
"Refresh APT index and verify repositories:": "Actualizar el índice APT y verificar los repositorios:",
"Refresh catalog": "Actualizar catálogo",
@@ -7966,6 +7970,7 @@
"Use a privileged LXC for NFS server/client workflows.": "Utiliza un LXC privilegiado para flujos de trabajo de servidor/cliente NFS.",
"Use a privileged LXC for Samba client/server workflows.": "Utiliza un LXC privilegiado para flujos de trabajo de cliente/servidor Samba.",
"Use a registry hostname, for example ghcr.io or registry.example.com:5000.": "Escribe el nombre de un registro, por ejemplo ghcr.io o registry.example.com:5000.",
"Use a trusted HTTPS reverse proxy. Upgrade Nextcloud one major version at a time.": "Usa un proxy inverso HTTPS con un certificado de confianza. Actualiza Nextcloud de una versión principal a la siguiente, sin saltarte ninguna.",
"Use an existing SSH private key file on this host": "Usar un archivo de clave privada SSH existente en este host",
"Use an existing keyfile": "Usar un archivo de claves existente",
"Use an image reference, for example ghcr.io/owner/app:latest.": "Escribe una referencia de imagen, por ejemplo ghcr.io/owner/app:latest.",
@@ -7979,6 +7984,7 @@
"Use existing filesystem": "Usar el sistema de archivos existente",
"Use existing: mount without formatting": "Usar existente: montar sin formatear",
"Use images only if the directory is on suitable storage.": "Utiliza imágenes solo si el directorio está en un almacenamiento adecuado.",
"Use managed storage for /config and MariaDB. A host/NAS directory can be selected for /data and needs a separate backup.": "Usa almacenamiento gestionado para /config y MariaDB. Para /data puedes elegir un directorio del host/NAS, que necesita su propia copia de seguridad.",
"Use mp1/mp2/... for extra mount points.": "Utiliza mp1/mp2/... para puntos de montaje adicionales.",
"Use only when unprivileged containers cannot meet your needs": "Úselo solo cuando los contenedores sin privilegios no puedan satisfacer sus necesidades",
"Use option 1 to add a local disk (as Proxmox storage and/or as a host mount).": "Utiliza la opción 1 para añadir un disco local (como almacenamiento Proxmox y/o como montaje de host).",
+6
View File
@@ -1644,6 +1644,7 @@
"Create scheduled backup job": "Créer une tâche de sauvegarde planifiée",
"Create share directory:": "Créer un répertoire de partage :",
"Create shared directory:": "Créer un répertoire partagé :",
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux génère le mot de passe de l'administrateur et l'affiche à la fin de l'installation. Modifiez-le après la première connexion.",
"Create this LXC in privileged mode?": "Créer ce LXC en mode privilégié ?",
"Create with create_user in the container console": "Créez l’utilisateur avec create_user dans la console du conteneur",
"Create, send and sign documents with a self-hosted web app.": "Créez, envoyez et signez des documents avec une application web auto-hébergée.",
@@ -3904,6 +3905,7 @@
"Linux/Mac path:": "Chemin Linux/Mac :",
"LinuxServer Jellyfin with optional GPU passthrough": "LinuxServer Jellyfin avec passage GPU optionnel",
"LinuxServer MariaDB requires a user, database and password": "LinuxServer MariaDB requires un utilisateur, une base de données et un mot de passe",
"LinuxServer Nextcloud with MariaDB and Redis": "Nextcloud de LinuxServer avec MariaDB et Redis",
"List Available Disks": "Liste des disques disponibles",
"List IOMMU group mapping": "Répertorier le mappage de groupe IOMMU",
"List NVMe devices": "Répertorier les appareils NVMe",
@@ -4484,6 +4486,7 @@
"Next Step Required": "Prochaine étape requise",
"Next Steps:": "Prochaines étapes :",
"Next step: stop that VM first, then run": "Étape suivante : arrêtez d'abord cette VM, puis exécutez",
"Nextcloud Web UI": "Interface web de Nextcloud",
"Nextcloud configuration cancelled": "Configuration Nextcloud annulée",
"Nextcloud gives you access to all your files wherever you are.": "Nextcloud vous donne accès à tous vos fichiers où que vous soyez.",
"Nextcloud volume size in GB": "Taille du volume Nextcloud en GB",
@@ -5605,6 +5608,7 @@
"Recreation prepared": "Loisirs préparés",
"Recursive DNS resolver.": "Résolveur DNS récursif.",
"Recursive and authoritative DNS server.": "Serveur DNS récursif et faisant autorité.",
"Redis locking is configured automatically. Existing Nextcloud configuration files are preserved.": "Le verrouillage avec Redis est configuré automatiquement. Les fichiers de configuration existants de Nextcloud sont conservés.",
"Redmine": "Redmine",
"Refresh APT index and verify repositories:": "Actualisez l'index APT et vérifiez les référentiels :",
"Refresh catalog": "Actualiser le catalogue",
@@ -7994,6 +7998,7 @@
"Use a privileged LXC for NFS server/client workflows.": "Utilisez un LXC privilégié pour les workflows serveur/client NFS.",
"Use a privileged LXC for Samba client/server workflows.": "Utilisez un LXC privilégié pour les workflows client/serveur Samba.",
"Use a registry hostname, for example ghcr.io or registry.example.com:5000.": "Utilisez un nom d'hôte de registre, par exemple ghcr.io ou registry.example.com:5000.",
"Use a trusted HTTPS reverse proxy. Upgrade Nextcloud one major version at a time.": "Utilisez un proxy inverse HTTPS avec un certificat de confiance. Mettez Nextcloud à niveau vers la version majeure suivante, sans en sauter.",
"Use an existing SSH private key file on this host": "Utiliser un fichier de clé privée SSH existant sur cet hôte",
"Use an existing keyfile": "utiliser un fichier de clés existant",
"Use an image reference, for example ghcr.io/owner/app:latest.": "Utilisez une référence d'image, par exemple ghcr.io/owner/app:latest.",
@@ -8007,6 +8012,7 @@
"Use existing filesystem": "Utiliser le système de fichiers existant",
"Use existing: mount without formatting": "Utiliser existant : monter sans formatage",
"Use images only if the directory is on suitable storage.": "Utilisez des images uniquement si le répertoire se trouve sur un stockage approprié.",
"Use managed storage for /config and MariaDB. A host/NAS directory can be selected for /data and needs a separate backup.": "Utilisez un stockage géré pour /config et MariaDB. Pour /data, vous pouvez choisir un répertoire de l’hôte/NAS, qui nécessite sa propre sauvegarde.",
"Use mp1/mp2/... for extra mount points.": "Utilisez mp1/mp2/... pour des points de montage supplémentaires.",
"Use only when unprivileged containers cannot meet your needs": "À utiliser uniquement lorsque les conteneurs non privilégiés ne peuvent pas répondre à vos besoins",
"Use option 1 to add a local disk (as Proxmox storage and/or as a host mount).": "Utilisez l'option 1 pour ajouter un disque local (comme stockage Proxmox et/ou comme montage hôte).",
+6
View File
@@ -1644,6 +1644,7 @@
"Create scheduled backup job": "Crea attività di backup pianificata",
"Create share directory:": "Crea directory condivisa:",
"Create shared directory:": "Crea directory condivisa:",
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux genera la password dell'amministratore e la mostra alla fine dell'installazione. Cambiala dopo il primo accesso.",
"Create this LXC in privileged mode?": "Creare questo LXC in modalità privilegiata?",
"Create with create_user in the container console": "Crea l’utente con create_user nella console del container",
"Create, send and sign documents with a self-hosted web app.": "Crea, invia e firma documenti con un'app web self-hosted.",
@@ -3904,6 +3905,7 @@
"Linux/Mac path:": "Percorso Linux/Mac:",
"LinuxServer Jellyfin with optional GPU passthrough": "LinuxServer Jellyfin con passthrough GPU opzionale",
"LinuxServer MariaDB requires a user, database and password": "LinuxServer MariaDB richiede un utente, un database e una password",
"LinuxServer Nextcloud with MariaDB and Redis": "Nextcloud di LinuxServer con MariaDB e Redis",
"List Available Disks": "Elenca i dischi disponibili",
"List IOMMU group mapping": "Elenca la mappatura dei gruppi IOMMU",
"List NVMe devices": "Elenca i dispositivi NVMe",
@@ -4484,6 +4486,7 @@
"Next Step Required": "Passaggio successivo obbligatorio",
"Next Steps:": "Passaggi successivi:",
"Next step: stop that VM first, then run": "Passaggio successivo: arresta prima la VM, quindi esegui",
"Nextcloud Web UI": "Interfaccia web di Nextcloud",
"Nextcloud configuration cancelled": "Configurazione di Nextcloud annullata",
"Nextcloud gives you access to all your files wherever you are.": "Nextcloud ti dà accesso a tutti i tuoi file, ovunque ti trovi.",
"Nextcloud volume size in GB": "Dimensione volume Nextcloud in GB",
@@ -5605,6 +5608,7 @@
"Recreation prepared": "Ricreazione preparata",
"Recursive DNS resolver.": "Resolver DNS ricorsivo.",
"Recursive and authoritative DNS server.": "Server DNS ricorsivo e autorevole.",
"Redis locking is configured automatically. Existing Nextcloud configuration files are preserved.": "Il blocco dei file con Redis viene configurato automaticamente. I file di configurazione esistenti di Nextcloud vengono conservati.",
"Redmine": "Redmine",
"Refresh APT index and verify repositories:": "Aggiorna l'indice APT e verifica i repository:",
"Refresh catalog": "Aggiorna catalogo",
@@ -7995,6 +7999,7 @@
"Use a privileged LXC for NFS server/client workflows.": "Usare un LXC privilegiato per le operazioni server/client NFS.",
"Use a privileged LXC for Samba client/server workflows.": "Usare un LXC privilegiato per le operazioni client/server Samba.",
"Use a registry hostname, for example ghcr.io or registry.example.com:5000.": "Utilizza un nome host del registro, ad esempio ghcr.io o registry.example.com:5000.",
"Use a trusted HTTPS reverse proxy. Upgrade Nextcloud one major version at a time.": "Usa un proxy inverso HTTPS con un certificato attendibile. Aggiorna Nextcloud alla versione principale successiva, senza saltarne nessuna.",
"Use an existing SSH private key file on this host": "Usa un file di chiave privata SSH esistente su questo host",
"Use an existing keyfile": "Usa file di chiavi esistente",
"Use an image reference, for example ghcr.io/owner/app:latest.": "Utilizza un riferimento a un'immagine, ad esempio ghcr.io/owner/app:latest.",
@@ -8008,6 +8013,7 @@
"Use existing filesystem": "Usa file system esistente",
"Use existing: mount without formatting": "Usa esistente: monta senza formattare",
"Use images only if the directory is on suitable storage.": "Usare images solo se la directory è su uno storage adatto.",
"Use managed storage for /config and MariaDB. A host/NAS directory can be selected for /data and needs a separate backup.": "Usa lo storage gestito per /config e MariaDB. Per /data puoi scegliere una directory dell’host/NAS, che richiede un backup separato.",
"Use mp1/mp2/... for extra mount points.": "Usare mp1/mp2/... per mount aggiuntivi.",
"Use only when unprivileged containers cannot meet your needs": "Usare solo se i container non privilegiati non soddisfano le esigenze",
"Use option 1 to add a local disk (as Proxmox storage and/or as a host mount).": "Usare l’opzione 1 per aggiungere un disco locale (come storage Proxmox e/o mount host).",
+6
View File
@@ -1644,6 +1644,7 @@
"Create scheduled backup job": "Criar tarefa de backup agendada",
"Create share directory:": "Crie um diretório de compartilhamento:",
"Create shared directory:": "Crie um diretório compartilhado:",
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "O ProxMenux gera a senha do administrador e mostra-a no fim da instalação. Altere-a após o primeiro login.",
"Create this LXC in privileged mode?": "Criar este LXC em modo privilegiado?",
"Create with create_user in the container console": "Crie o utilizador com create_user na consola do contentor",
"Create, send and sign documents with a self-hosted web app.": "Crie, envie e assine documentos com uma aplicação web autoalojada.",
@@ -3904,6 +3905,7 @@
"Linux/Mac path:": "Caminho Linux/Mac:",
"LinuxServer Jellyfin with optional GPU passthrough": "LinuxServer Jellyfin com passagem opcional de GPU",
"LinuxServer MariaDB requires a user, database and password": "LinuxServer MariaDB requires um usuário, banco de dados e senha",
"LinuxServer Nextcloud with MariaDB and Redis": "Nextcloud da LinuxServer com MariaDB e Redis",
"List Available Disks": "Listar discos disponíveis",
"List IOMMU group mapping": "Listar mapeamento de grupo IOMMU",
"List NVMe devices": "Listar dispositivos NVMe",
@@ -4484,6 +4486,7 @@
"Next Step Required": "Próxima etapa obrigatória",
"Next Steps:": "Próximas etapas:",
"Next step: stop that VM first, then run": "Próxima etapa: pare a VM primeiro e depois execute",
"Nextcloud Web UI": "Interface web do Nextcloud",
"Nextcloud configuration cancelled": "Configuração do Nextcloud cancelada",
"Nextcloud gives you access to all your files wherever you are.": "O Nextcloud dá-lhe acesso a todos os seus ficheiros onde quer que esteja.",
"Nextcloud volume size in GB": "Tamanho do volume Nextcloud em GB",
@@ -5605,6 +5608,7 @@
"Recreation prepared": "Recreação preparada",
"Recursive DNS resolver.": "Resolvedor DNS recursivo.",
"Recursive and authoritative DNS server.": "Servidor DNS recursivo e autoritativo.",
"Redis locking is configured automatically. Existing Nextcloud configuration files are preserved.": "O bloqueio de ficheiros com Redis é configurado automaticamente. Os ficheiros de configuração existentes do Nextcloud são preservados.",
"Redmine": "Redmine",
"Refresh APT index and verify repositories:": "Atualize o índice APT e verifique os repositórios:",
"Refresh catalog": "Atualizar catálogo",
@@ -7995,6 +7999,7 @@
"Use a privileged LXC for NFS server/client workflows.": "Use um LXC privilegiado para fluxos de trabalho de servidor/cliente NFS.",
"Use a privileged LXC for Samba client/server workflows.": "Use um LXC privilegiado para fluxos de trabalho cliente/servidor Samba.",
"Use a registry hostname, for example ghcr.io or registry.example.com:5000.": "Use um nome de host de registro, por exemplo ghcr.io ou registry.example.com:5000.",
"Use a trusted HTTPS reverse proxy. Upgrade Nextcloud one major version at a time.": "Use um proxy inverso HTTPS com um certificado de confiança. Atualize o Nextcloud para a versão principal seguinte, sem saltar nenhuma.",
"Use an existing SSH private key file on this host": "Use um arquivo de chave privada SSH existente neste host",
"Use an existing keyfile": "Use um arquivo-chave existente",
"Use an image reference, for example ghcr.io/owner/app:latest.": "Use uma referência de imagem, por exemplo ghcr.io/owner/app:latest.",
@@ -8008,6 +8013,7 @@
"Use existing filesystem": "Use o sistema de arquivos existente",
"Use existing: mount without formatting": "Use existente: monte sem formatação",
"Use images only if the directory is on suitable storage.": "Use imagens somente se o diretório estiver em um armazenamento adequado.",
"Use managed storage for /config and MariaDB. A host/NAS directory can be selected for /data and needs a separate backup.": "Use armazenamento gerido para /config e MariaDB. Para /data pode escolher um diretório do host/NAS, que precisa de uma cópia de segurança separada.",
"Use mp1/mp2/... for extra mount points.": "Use mp1/mp2/... para pontos de montagem extras.",
"Use only when unprivileged containers cannot meet your needs": "Use somente quando contêineres sem privilégios não atenderem às suas necessidades",
"Use option 1 to add a local disk (as Proxmox storage and/or as a host mount).": "Use a opção 1 para adicionar um disco local (como armazenamento Proxmox e/ou como montagem de host).",
+6
View File
@@ -1644,6 +1644,7 @@
"Create scheduled backup job": "Vytvoriť naplánovanú úlohu zálohy",
"Create share directory:": "Vytvoriť zdieľaný priečinok:",
"Create shared directory:": "Vytvoriť zdieľaný priečinok:",
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux vygeneruje heslo správcu a zobrazí ho na konci inštalácie. Po prvom prihlásení ho zmeňte.",
"Create this LXC in privileged mode?": "Vytvoriť tento LXC v privilegovanom režime?",
"Create with create_user in the container console": "Používateľa vytvorte príkazom create_user v konzole kontajnera",
"Create, send and sign documents with a self-hosted web app.": "Vytvárajte, posielajte a podpisujte dokumenty vo webovej aplikácii prevádzkovanej vo vlastnej réžii.",
@@ -3904,6 +3905,7 @@
"Linux/Mac path:": "Cesta pre Linux/Mac:",
"LinuxServer Jellyfin with optional GPU passthrough": "LinuxServer Jellyfin s voliteľným GPU priechodom",
"LinuxServer MariaDB requires a user, database and password": "LinuxServer MariaDB vyžaduje používateľa, databázu a heslo",
"LinuxServer Nextcloud with MariaDB and Redis": "Nextcloud od LinuxServer s MariaDB a Redis",
"List Available Disks": "Zobraziť dostupné disky",
"List IOMMU group mapping": "Zobraziť mapovanie IOMMU skupín",
"List NVMe devices": "Zobraziť NVMe zariadenia",
@@ -4484,6 +4486,7 @@
"Next Step Required": "Je potrebný ďalší krok",
"Next Steps:": "Ďalšie kroky:",
"Next step: stop that VM first, then run": "Ďalší krok: najprv zastavte tú VM, potom spustite",
"Nextcloud Web UI": "Webové rozhranie Nextcloud",
"Nextcloud configuration cancelled": "Konfigurácia Nextcloud zrušená",
"Nextcloud gives you access to all your files wherever you are.": "Nextcloud vám umožní prístup ku všetkým súborom kdekoľvek ste.",
"Nextcloud volume size in GB": "Veľkosť dátového zväzku Nextcloud v GB",
@@ -5607,6 +5610,7 @@
"Recreation prepared": "Pripravené rekreácie",
"Recursive DNS resolver.": "Rekurzívny DNS resolver.",
"Recursive and authoritative DNS server.": "Rekurzívny a autoritatívny DNS server.",
"Redis locking is configured automatically. Existing Nextcloud configuration files are preserved.": "Zamykanie súborov pomocou Redis sa nastaví automaticky. Existujúce konfiguračné súbory Nextcloud sa zachovajú.",
"Redmine": "Redmine",
"Refresh APT index and verify repositories:": "Obnovte APT index a overte repozitáre:",
"Refresh catalog": "Aktualizovať katalóg",
@@ -7995,6 +7999,7 @@
"Use a privileged LXC for NFS server/client workflows.": "Pre NFS server/klient používajte privilegovaný LXC.",
"Use a privileged LXC for Samba client/server workflows.": "Pre Samba klient/server používajte privilegovaný LXC.",
"Use a registry hostname, for example ghcr.io or registry.example.com:5000.": "Použite názov hostiteľa registra, napríklad ghcr.io alebo registry.example.com:5000.",
"Use a trusted HTTPS reverse proxy. Upgrade Nextcloud one major version at a time.": "Použite reverzný proxy server HTTPS s dôveryhodným certifikátom. Nextcloud aktualizujte vždy na nasledujúcu hlavnú verziu, žiadnu nepreskakujte.",
"Use an existing SSH private key file on this host": "Použiť existujúci súbor súkromného SSH kľúča na tomto hostovi",
"Use an existing keyfile": "Použiť existujúci keyfile",
"Use an image reference, for example ghcr.io/owner/app:latest.": "Použite odkaz na obraz, napríklad ghcr.io/owner/app:latest.",
@@ -8008,6 +8013,7 @@
"Use existing filesystem": "Použiť existujúci súborový systém",
"Use existing: mount without formatting": "Použiť existujúci: pripojiť bez formátovania",
"Use images only if the directory is on suitable storage.": "Typ images používajte iba vtedy, ak je priečinok na vhodnom úložisku.",
"Use managed storage for /config and MariaDB. A host/NAS directory can be selected for /data and needs a separate backup.": "Pre /config a MariaDB použite spravované úložisko. Pre /data môžete vybrať adresár hostiteľa/NAS, ktorý vyžaduje samostatné zálohovanie.",
"Use mp1/mp2/... for extra mount points.": "Pre ďalšie mount pointy použite mp1/mp2/...",
"Use only when unprivileged containers cannot meet your needs": "Použite iba vtedy, keď neprivilegované kontajnery nestačia vašim potrebám",
"Use option 1 to add a local disk (as Proxmox storage and/or as a host mount).": "Použite možnosť 1 na pridanie lokálneho disku (ako úložisko Proxmoxu a/alebo ako mount na hostovi).",
+6
View File
@@ -1644,6 +1644,7 @@
"Create scheduled backup job": "Skapa schemalagt säkerhetskopieringsjobb",
"Create share directory:": "Skapa delningskatalog:",
"Create shared directory:": "Skapa delad katalog:",
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux genererar administratörslösenordet och visar det i slutet av installationen. Ändra det efter första inloggningen.",
"Create this LXC in privileged mode?": "Skapa denna LXC i privilegierat läge?",
"Create with create_user in the container console": "Skapa användaren med create_user i containerns konsol",
"Create, send and sign documents with a self-hosted web app.": "Skapa, skicka och signera dokument med en webbapp som du driftar själv.",
@@ -3904,6 +3905,7 @@
"Linux/Mac path:": "Linux/Mac sökväg:",
"LinuxServer Jellyfin with optional GPU passthrough": "LinuxServer Jellyfin med valfri GPU genomgång",
"LinuxServer MariaDB requires a user, database and password": "LinuxServer MariaDB requires en användare, databas och lösenord",
"LinuxServer Nextcloud with MariaDB and Redis": "LinuxServer Nextcloud med MariaDB och Redis",
"List Available Disks": "Lista tillgängliga diskar",
"List IOMMU group mapping": "Lista IOMMU-gruppkartläggning",
"List NVMe devices": "Lista NVMe-enheter",
@@ -4484,6 +4486,7 @@
"Next Step Required": "Nästa steg krävs",
"Next Steps:": "Nästa steg:",
"Next step: stop that VM first, then run": "Nästa steg: stoppa den virtuella datorn först och kör sedan",
"Nextcloud Web UI": "Nextclouds webbgränssnitt",
"Nextcloud configuration cancelled": "Nextcloud konfiguration annullerad",
"Nextcloud gives you access to all your files wherever you are.": "Nextcloud ger dig tillgång till alla dina filer var du än är.",
"Nextcloud volume size in GB": "Nextcloud volymstorlek i GB",
@@ -5605,6 +5608,7 @@
"Recreation prepared": "Rekreation förberedd",
"Recursive DNS resolver.": "Rekursiv DNS-resolver.",
"Recursive and authoritative DNS server.": "Rekursiv och auktoritativ DNS-server.",
"Redis locking is configured automatically. Existing Nextcloud configuration files are preserved.": "Fillåsning med Redis konfigureras automatiskt. Befintliga konfigurationsfiler för Nextcloud bevaras.",
"Redmine": "Redmine",
"Refresh APT index and verify repositories:": "Uppdatera APT-index och verifiera förråd:",
"Refresh catalog": "Uppdatera katalogen",
@@ -7995,6 +7999,7 @@
"Use a privileged LXC for NFS server/client workflows.": "Använd en privilegierad LXC för NFS-server/klient-arbetsflöden.",
"Use a privileged LXC for Samba client/server workflows.": "Använd en privilegierad LXC för Samba klient/server arbetsflöden.",
"Use a registry hostname, for example ghcr.io or registry.example.com:5000.": "Använd ett registervärdnamn, till exempel ghcr.io eller registry.example.com:5000.",
"Use a trusted HTTPS reverse proxy. Upgrade Nextcloud one major version at a time.": "Använd en omvänd HTTPS-proxy med ett betrott certifikat. Uppgradera Nextcloud till nästa huvudversion utan att hoppa över någon.",
"Use an existing SSH private key file on this host": "Använd en befintlig privat SSH-nyckelfil på denna värd",
"Use an existing keyfile": "Använd en befintlig nyckelfil",
"Use an image reference, for example ghcr.io/owner/app:latest.": "Använd en bildreferens, till exempel ghcr.io/owner/app:latest.",
@@ -8008,6 +8013,7 @@
"Use existing filesystem": "Använd befintligt filsystem",
"Use existing: mount without formatting": "Använd befintlig: montera utan formatering",
"Use images only if the directory is on suitable storage.": "Använd endast bilder om katalogen finns på lämplig lagring.",
"Use managed storage for /config and MariaDB. A host/NAS directory can be selected for /data and needs a separate backup.": "Använd hanterad lagring för /config och MariaDB. För /data kan du välja en katalog på värden/NAS som behöver säkerhetskopieras separat.",
"Use mp1/mp2/... for extra mount points.": "Använd mp1/mp2/... för extra monteringspunkter.",
"Use only when unprivileged containers cannot meet your needs": "Använd endast när oprivilegierade behållare inte kan uppfylla dina behov",
"Use option 1 to add a local disk (as Proxmox storage and/or as a host mount).": "Använd alternativ 1 för att lägga till en lokal disk (som Proxmox-lagring och/eller som värdmontering).",
+1 -1
View File
@@ -2,7 +2,7 @@
<!-- Generated from oci/catalog/verification.json by .github/scripts/oci_validation.py. Do not edit by hand. -->
52 of 554 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.
52 of 555 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.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+32
View File
@@ -19414,6 +19414,38 @@
"optional_relaxed_confinement": false,
"requires_security_confirmation": false,
"hidden": false
},
{
"id": "nextcloud-mariadb",
"provider": "linuxserver",
"template_family": "curated-profile",
"title": "Nextcloud MariaDB",
"repository": "https://github.com/linuxserver/docker-nextcloud",
"description": "LinuxServer Nextcloud, MariaDB 10.11 and Redis 7 in three native OCI LXC containers. /config and /data are separate; user files can use a host/NAS directory while database state stays in local managed storage.",
"website": "https://nextcloud.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nextcloud.webp",
"architectures": [
"amd64",
"arm64"
],
"updated_at": null,
"main_image": "lscr.io/linuxserver/nextcloud:latest",
"category": "productivity",
"category_label": "Productivity & Workflows",
"replaces_discovered_ids": [],
"curated_path": "catalog/curated/nextcloud-mariadb.json",
"template_id": "image-nextcloud-mariadb",
"template": "apps/nextcloud-mariadb.json",
"template_status": "generated-unvalidated",
"automatic_install_candidate": true,
"untranslated_blockers": [],
"requires_privileged_lxc": false,
"optional_privileged_lxc": false,
"requires_host_pid_namespace": false,
"requires_relaxed_confinement": false,
"optional_relaxed_confinement": false,
"requires_security_confirmation": false,
"hidden": false
}
],
"discovery": {
+3
View File
@@ -161,6 +161,9 @@
],
"wordcraft": [
"/storage"
],
"nextcloud-mariadb": [
"/data"
]
},
"sizes": {
@@ -0,0 +1,37 @@
<?php
// Prepare a fresh LinuxServer Nextcloud without replacing persistent user settings.
$dir = '/config/www/nextcloud/config';
umask(0077);
if (!is_dir($dir) && !mkdir($dir, 0770, true)) { throw new RuntimeException('Cannot create the Nextcloud config directory'); }
// The directory belongs to the application user. Create each file without
// following a link placed there, and give it to that user without following one.
function create_private($path, $content, $owner) {
$handle = @fopen($path, 'x');
if ($handle === false || fwrite($handle, $content) !== strlen($content) || !fclose($handle)) { throw new RuntimeException('Cannot write ' . basename($path)); }
if ($owner && (!lchown($path, fileowner($owner)) || !lchgrp($path, filegroup($owner)))) { throw new RuntimeException('Cannot set Nextcloud configuration ownership'); }
}
if (!is_file($dir . '/config.php') && !is_file($dir . '/autoconfig.php')) {
foreach (['MYSQL_HOST', 'MYSQL_DATABASE', 'MYSQL_USER', 'MYSQL_PASSWORD', 'NEXTCLOUD_ADMIN_PASSWORD'] as $name) {
if (getenv($name) === false || getenv($name) === '') { throw new RuntimeException('Missing Nextcloud setting: ' . $name); }
}
// Keep the defaults that LinuxServer copies only into an empty config directory.
$defaults = '/app/www/src/config';
if (is_dir($defaults)) {
foreach (new DirectoryIterator($defaults) as $file) {
if ($file->isDot() || !$file->isFile()) { continue; }
$target = $dir . '/' . $file->getFilename();
if (!file_exists($target) && !is_link($target)) { create_private($target, file_get_contents($file->getPathname()), null); }
}
}
// With the administrator included, Nextcloud installs itself on the first
// request, so nobody else on the network can claim the setup page.
$settings = ['dbtype' => 'mysql', 'dbhost' => getenv('MYSQL_HOST'), 'dbname' => getenv('MYSQL_DATABASE'), 'dbuser' => getenv('MYSQL_USER'), 'dbpass' => getenv('MYSQL_PASSWORD'), 'directory' => '/data', 'adminlogin' => 'admin', 'adminpass' => getenv('NEXTCLOUD_ADMIN_PASSWORD')];
// Existing installations skip LinuxServer's first-run ownership setup.
create_private($dir . '/autoconfig.php', "<?php\n\$AUTOCONFIG = " . var_export($settings, true) . ";\n", $dir);
}
$path = $dir . '/proxmenux-redis.config.php';
// Installed instances keep their existing cache configuration unchanged.
if (!is_file($path) && !is_link($path) && !is_file($dir . '/config.php')) {
$settings = ['filelocking.enabled' => true, 'memcache.local' => '\OC\Memcache\APCu', 'memcache.locking' => '\OC\Memcache\Redis', 'redis' => ['host' => 'redis', 'port' => 6379, 'timeout' => 0.0]];
create_private($path, "<?php\n\$CONFIG = " . var_export($settings, true) . ";\n", $dir);
}
+9 -2
View File
@@ -247,12 +247,19 @@ def relay_child_output(stream):
return result
def http_url(scheme, host, port, path):
"""Without the default port of its scheme: an application that records its
first request, like Nextcloud's trusted domain, sees the host a browser sends."""
default = (scheme, int(port)) in (('https', 443), ('http', 80))
return f"{scheme}://{host}{'' if default else f':{port}'}{path}"
def assign_health_address(service):
"""Bind a generated dependency probe to its allocated private address."""
check = service['healthcheck']
if check['type'] == 'http':
endpoint = check['endpoint']
check['url'] = f"{endpoint['scheme']}://{service['ip']}:{endpoint['port']}{endpoint['path']}"
check['url'] = http_url(endpoint['scheme'], service['ip'], endpoint['port'], endpoint['path'])
elif check['type'] == 'tcp':
check['host'] = service['ip']
@@ -383,7 +390,7 @@ def lan_access_urls(services, subnet, strict=True):
if not candidates:
raise RuntimeError(f"{translate('No LAN address was obtained for the service:')} {service['name']}")
urls.append({'label':service['name'],
'url':f"{endpoint['scheme']}://{candidates[0]}:{endpoint['port']}{endpoint['path']}"})
'url':http_url(endpoint['scheme'], candidates[0], endpoint['port'], endpoint['path'])})
except Exception:
if strict:
raise
+14 -2
View File
@@ -373,6 +373,18 @@ healthcheck_probe_url() {
&& (( status == 1 || status == 8 || status == 52 || status == 56 ))
}
# The address of the check, without the default port of its scheme: an
# application that records its first request, like Nextcloud's trusted domain,
# then sees the same host a browser sends.
healthcheck_url() {
local port=$1
if [[ ( $HC_SCHEME == https && $port == 443 ) || ( $HC_SCHEME == http && $port == 80 ) ]]; then
printf '%s://%s%s' "$HC_SCHEME" "$IP" "$HC_PATH"
else
printf '%s://%s:%s%s' "$HC_SCHEME" "$IP" "$port" "$HC_PATH"
fi
}
# Any of the addresses answering means the application is up; HC_URL keeps the
# one that answered.
healthcheck_probe() {
@@ -2351,12 +2363,12 @@ if [[ $START_AFTER == 1 && $HAS_STARTUP_HEALTHCHECK == 1 ]]; then
(( HC_STABILITY < HC_TIMEOUT )) \
|| die "$(translate "The stability period must be shorter than the healthcheck timeout")"
[[ -n $IP ]] || die "$(translate "The healthcheck cannot run without an IP address")"
HC_URL="${HC_SCHEME}://${IP}:${HC_PORT}${HC_PATH}"
HC_URL=$(healthcheck_url "$HC_PORT")
HC_URLS=("$HC_URL")
while IFS= read -r HC_ALT_PORT; do
[[ $HC_ALT_PORT =~ ^[0-9]+$ && $HC_ALT_PORT -ge 1 && $HC_ALT_PORT -le 65535 ]] \
|| die "$(translate "Invalid healthcheck port")"
HC_URLS+=("${HC_SCHEME}://${IP}:${HC_ALT_PORT}${HC_PATH}")
HC_URLS+=("$(healthcheck_url "$HC_ALT_PORT")")
done < <(jq -r '.alternative_ports[]? | tostring' <<<"$STARTUP_HEALTHCHECK")
HC_ANY_STATUS=$(jq -r '.accept_any_status // false' <<<"$STARTUP_HEALTHCHECK")
HC_ACCEPT_NON_HTTP=$(jq -r '.accept_non_http // false' <<<"$STARTUP_HEALTHCHECK")
@@ -0,0 +1,34 @@
"""The startup check asks for the address a browser would use: without the
default port of its scheme. Nextcloud trusts the host of its first request."""
from pathlib import Path
import re
import subprocess
import unittest
SCRIPT = (Path(__file__).resolve().parents[1] / 'remote/install_oci.sh').read_text()
def url(scheme, port, path='/'):
function = re.search(r'^healthcheck_url\(\) \{\n.*?^\}\n', SCRIPT, re.M | re.S).group(0)
done = subprocess.run(['bash', '-c', function + f'HC_SCHEME={scheme} IP=192.0.2.10 HC_PATH={path}; healthcheck_url {port}'],
capture_output=True, text=True, check=True)
return done.stdout
class HealthcheckUrlTests(unittest.TestCase):
def test_default_ports_are_left_out(self):
self.assertEqual(url('https', 443), 'https://192.0.2.10/')
self.assertEqual(url('http', 80, '/status'), 'http://192.0.2.10/status')
def test_other_ports_are_kept(self):
self.assertEqual(url('http', 443), 'http://192.0.2.10:443/')
self.assertEqual(url('https', 8443), 'https://192.0.2.10:8443/')
self.assertEqual(url('http', 3000), 'http://192.0.2.10:3000/')
def test_every_probe_address_uses_it(self):
self.assertIn('HC_URL=$(healthcheck_url "$HC_PORT")', SCRIPT)
self.assertIn('HC_URLS+=("$(healthcheck_url "$HC_ALT_PORT")")', SCRIPT)
if __name__ == '__main__':
unittest.main()
+2 -1
View File
@@ -18,7 +18,8 @@ class MainFrontendReadiness(unittest.TestCase):
with patch.object(installer,'run',return_value='10.77.6.30\n172.30.249.70\n'), \
patch.object(installer,'wait_web') as web:
url=installer.wait_main_web(service,ipaddress.ip_network('10.77.6.0/24'))
self.assertEqual(url,'http://172.30.249.70:80/')
# Without the default port: the host a browser sends.
self.assertEqual(url,'http://172.30.249.70/')
self.assertEqual(service['healthcheck']['url'],url)
self.assertEqual(service['ip'],'10.77.6.30')
web.assert_called_once_with(service,url)
+214
View File
@@ -0,0 +1,214 @@
"""The LinuxServer/MariaDB variant keeps its complete private stack and safe bootstrap."""
import json
import base64
import re
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import unittest
from contextlib import ExitStack
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(ROOT / 'src'), str(Path(__file__).parent)]
from proxmenux_oci.catalog import Catalog
from proxmenux_oci.custom import template_from_oci_compose
from proxmenux_oci.installer import ADVANCED_MODE, DEFAULT_MODE, build_deployment
from proxmenux_oci.stack import assess
from test_advanced_flow_order import RecordingUI, storages, addresses
class NextcloudMariaDBTests(unittest.TestCase):
def setUp(self):
self.catalog = Catalog(ROOT)
self.template = self.catalog.compose('nextcloud-mariadb')
def test_catalog_roundtrip_preserves_the_original_image_families_and_tls(self):
t = template_from_oci_compose(json.dumps(self.template))
self.catalog.validate(t)
self.assertEqual(assess(t), [])
services = {s['name']: s for s in t['compose_stack']['services']}
self.assertEqual({k: v['image'] for k, v in services.items()}, {
'nextcloud': 'lscr.io/linuxserver/nextcloud:latest',
'db': 'mariadb:10.11', 'redis': 'redis:7-alpine'})
self.assertEqual(t['first_run']['endpoints'][0]['scheme'], 'https')
self.assertEqual(t['first_run']['endpoints'][0]['port'], 443)
self.assertEqual(t['first_run']['credentials'][0]['password_environment'], 'NEXTCLOUD_ADMIN_PASSWORD')
self.assertEqual(t['first_run']['credentials'][0]['username'], 'admin')
self.assertTrue(t['catalog_ui']['icon'].startswith('https://'))
self.assertEqual(set(services['nextcloud']['depends_on']), {'db', 'redis'})
for name in ('db', 'redis'):
self.assertFalse(services[name]['compose'].get('ports'))
self.assertFalse(services[name]['frontend_network'])
def plan(self, mode, answers=None):
with ExitStack() as stack:
for target, value in [
('proxmenux_oci.installer.host.storages', storages),
('proxmenux_oci.installer.access.ask_addresses', addresses)]:
stack.enter_context(patch(target, side_effect=value))
for target, value in [
('proxmenux_oci.i18n.language', 'en'),
('proxmenux_oci.installer.host.bridges', [{'iface': 'vmbr0', 'cidr': '192.0.2.1/24'}]),
('proxmenux_oci.installer.host.timezone', 'Europe/Madrid')]:
stack.enter_context(patch(target, return_value=value))
return build_deployment(self.template, RecordingUI(answers), mode)
def test_default_and_advanced_plans_keep_shared_secrets_private_dependencies_and_persistence(self):
for mode in (DEFAULT_MODE, ADVANCED_MODE):
with self.subTest(mode=mode):
plan = self.plan(mode)
services = {s['name']: s for s in plan['services']}
env = {name: {e['name']: e['value'] for e in s['deployment']['environment']}
for name, s in services.items()}
self.assertEqual(env['nextcloud']['MYSQL_PASSWORD'], env['db']['MYSQL_PASSWORD'])
self.assertNotEqual(env['db']['MYSQL_PASSWORD'], env['db']['MYSQL_ROOT_PASSWORD'])
# The administrator is created by ProxMenux, not by whoever opens the page first.
self.assertEqual(len(env['nextcloud']['NEXTCLOUD_ADMIN_PASSWORD']), 64)
self.assertNotIn(env['nextcloud']['NEXTCLOUD_ADMIN_PASSWORD'],
(env['db']['MYSQL_PASSWORD'], env['db']['MYSQL_ROOT_PASSWORD']))
self.assertNotIn('NEXTCLOUD_ADMIN_PASSWORD', env['db'])
self.assertEqual(env['nextcloud']['MYSQL_HOST'], 'db')
self.assertEqual([s['name'] for s in plan['services']], ['db', 'redis', 'nextcloud'])
self.assertEqual([s['name'] for s in plan['services'] if s.get('frontend')], ['nextcloud'])
self.assertEqual(services['db']['healthcheck']['argv'][0], 'healthcheck.sh')
self.assertEqual(services['nextcloud']['healthcheck']['endpoint']['scheme'], 'https')
self.assertEqual({k: s['deployment']['hostname'] for k, s in services.items()},
{'nextcloud': 'nextcloud', 'db': 'nextcloud-db', 'redis': 'nextcloud-redis'})
for service in services.values():
self.assertTrue(service['deployment']['security']['unprivileged'])
self.assertTrue(all(m['backup'] for m in service['deployment']['mounts']))
self.assertEqual({k: v['deployment']['resources']['memory_mb'] for k, v in services.items()},
{'nextcloud': 2048, 'db': 1024, 'redis': 512})
runtime = services['nextcloud']['template']['proxmox']['installer_profile']['runtime']
self.assertIn('exec /init', runtime['compose_entrypoint'][2])
# The private umask of the bootstrap does not reach the application.
self.assertTrue(runtime['compose_entrypoint'][2].startswith('(umask 077; '))
self.assertTrue(runtime['compose_entrypoint'][2].endswith('| php); exec /init'))
self.assertNotIn('\n', runtime['compose_entrypoint'][2])
self.assertNotIn('\r', runtime['compose_entrypoint'][2])
def test_nas_mount_is_separate_from_configuration_database_and_cache(self):
plan = self.plan(DEFAULT_MODE, {'Where to store nextcloud: /data': 'host-bind',
'Host path for nextcloud: /data': '/mnt/pve/media/Nextcloud'})
services = {s['name']: s for s in plan['services']}
mounts = {m['container_path']: m for m in services['nextcloud']['deployment']['mounts']}
self.assertEqual(mounts['/data']['source'], '/mnt/pve/media/Nextcloud')
self.assertEqual(mounts['/data']['type'], 'host-bind')
self.assertFalse(mounts['/data']['backup'])
self.assertEqual(mounts['/config']['type'], 'managed-volume')
for name in ('db', 'redis'):
self.assertTrue(all(m['type'] == 'managed-volume' and m['backup']
for m in services[name]['deployment']['mounts']))
@unittest.skipUnless(shutil.which('php'), 'PHP CLI is needed to execute the bootstrap contract')
def test_bootstrap_prefills_mysql_with_escaped_secrets_and_preserves_existing_files(self):
entry = next(s for s in self.template['compose_stack']['services'] if s['name'] == 'nextcloud')['compose']['entrypoint']
with tempfile.TemporaryDirectory() as temp:
folder = Path(temp) / 'config'
image_config = Path(temp) / 'image-config'
image_config.mkdir()
defaults = {'CAN_INSTALL': '', '.htaccess': 'deny all', 'config.sample.php': '<?php // sample'}
for name, content in defaults.items(): (image_config / name).write_text(content)
payload = re.search(r'printf %s ([A-Za-z0-9+/=]+) \| base64 -d \| php', entry[2]).group(1)
source = base64.b64decode(payload).decode()
self.assertEqual(source, (ROOT / 'examples/nextcloud-mariadb-bootstrap.php').read_text())
test_source = source.replace('/config/www/nextcloud/config', str(folder)).replace('/app/www/src/config', str(image_config))
script = entry[2].replace(payload, base64.b64encode(test_source.encode()).decode()).replace('exec /init', 'exit 0')
password = "test-'quote\\backslash$not-a-real-secret\nline"
env = dict(os.environ, MYSQL_HOST='db', MYSQL_DATABASE='nextcloud', MYSQL_USER='nextcloud', MYSQL_PASSWORD=password,
NEXTCLOUD_ADMIN_PASSWORD='admin-' + password)
subprocess.run(['/bin/sh', '-ec', script], env=env, check=True, capture_output=True)
for name, content in defaults.items(): self.assertEqual((folder / name).read_text(), content)
autoconfig = folder / 'autoconfig.php'
redis = folder / 'proxmenux-redis.config.php'
for path in (autoconfig, redis):
subprocess.run(['php', '-l', str(path)], check=True, capture_output=True)
self.assertEqual(path.stat().st_mode & 0o777, 0o600)
code = 'require $argv[1]; echo json_encode($AUTOCONFIG);'
values = json.loads(subprocess.check_output(['php', '-r', code, str(autoconfig)], env=env))
self.assertEqual(values['dbpass'], password)
self.assertEqual(values['dbhost'], 'db')
self.assertEqual(values['directory'], '/data')
self.assertEqual((values['adminlogin'], values['adminpass']), ('admin', 'admin-' + password))
cache = json.loads(subprocess.check_output(['php', '-r', 'require $argv[1]; echo json_encode($CONFIG);', str(redis)]))
self.assertEqual(cache['redis']['host'], 'redis')
self.assertEqual(cache['memcache.locking'], '\\OC\\Memcache\\Redis')
# A retry/update must not replace user edits or a completed installation.
saved = {p: p.read_bytes() for p in (autoconfig, redis)}
subprocess.run(['/bin/sh', '-ec', script], env=dict(env, MYSQL_PASSWORD='changed'), check=True, capture_output=True)
for path, content in saved.items(): self.assertEqual(path.read_bytes(), content)
autoconfig.unlink()
(folder / 'CAN_INSTALL').unlink()
(folder / 'config.php').write_text('<?php $CONFIG = ["installed" => true];')
subprocess.run(['/bin/sh', '-ec', script], env=env, check=True, capture_output=True)
self.assertFalse(autoconfig.exists())
self.assertFalse((folder / 'CAN_INSTALL').exists())
@unittest.skipUnless(shutil.which('php'), 'PHP CLI is needed to execute the bootstrap contract')
def test_bootstrap_refuses_links_and_needs_the_administrator_password(self):
source = (ROOT / 'examples/nextcloud-mariadb-bootstrap.php').read_text()
base = dict(os.environ, MYSQL_HOST='db', MYSQL_DATABASE='nextcloud', MYSQL_USER='nextcloud', MYSQL_PASSWORD='x')
with tempfile.TemporaryDirectory() as temp:
folder = Path(temp) / 'config'
folder.mkdir()
target = Path(temp) / 'outside'
(folder / 'autoconfig.php').symlink_to(target)
script = source.replace('/config/www/nextcloud/config', str(folder)).replace('/app/www/src/config', str(Path(temp) / 'none'))
done = subprocess.run(['php'], input=script.encode(), env=dict(base, NEXTCLOUD_ADMIN_PASSWORD='y'), capture_output=True)
self.assertNotEqual(done.returncode, 0)
self.assertFalse(target.exists())
(folder / 'autoconfig.php').unlink()
done = subprocess.run(['php'], input=script.encode(), env=base, capture_output=True)
self.assertNotEqual(done.returncode, 0)
self.assertFalse((folder / 'autoconfig.php').exists())
@unittest.skipUnless(sys.platform.startswith('linux') and os.geteuid() == 0 and shutil.which('php'),
'Linux root is needed to reproduce existing-volume ownership')
def test_existing_config_volume_can_read_new_redis_configuration_as_its_app_user(self):
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
root.chmod(0o755)
folder = root / 'config'
folder.mkdir(mode=0o755)
os.chown(folder, 1001, 1001)
installed = folder / 'autoconfig.php'
installed.write_text('<?php $CONFIG = ["installed" => true];')
installed.chmod(0o640)
os.chown(installed, 1001, 1001)
source = (ROOT / 'examples/nextcloud-mariadb-bootstrap.php').read_text()
source = source.replace('/config/www/nextcloud/config', str(folder))
subprocess.run(['php'], input=source.encode(), check=True, capture_output=True)
redis = folder / 'proxmenux-redis.config.php'
self.assertEqual((redis.stat().st_uid, redis.stat().st_gid), (1001, 1001))
self.assertEqual(redis.stat().st_mode & 0o777, 0o600)
def app_user():
os.setgroups([])
os.setgid(1001)
os.setuid(1001)
result = subprocess.check_output(['php', '-r',
'require $argv[1]; echo json_encode($CONFIG);', str(redis)], preexec_fn=app_user)
self.assertEqual(json.loads(result)['redis']['host'], 'redis')
self.assertEqual(installed.read_text(), '<?php $CONFIG = ["installed" => true];')
self.assertFalse((folder / 'config.php').exists())
@unittest.skipUnless(shutil.which('php'), 'PHP CLI needed for the installed-instance contract')
def test_existing_installation_does_not_override_user_cache_settings(self):
with tempfile.TemporaryDirectory() as temp:
folder = Path(temp) / 'config'
folder.mkdir()
config = folder / 'config.php'
content = '<?php $CONFIG = ["installed" => true, "redis" => ["host" => "user-cache", "timeout" => 3]];'
config.write_text(content)
source = (ROOT / 'examples/nextcloud-mariadb-bootstrap.php').read_text()
source = source.replace('/config/www/nextcloud/config', str(folder))
subprocess.run(['php'], input=source.encode(), check=True, capture_output=True)
self.assertEqual(config.read_text(), content)
self.assertFalse((folder / 'proxmenux-redis.config.php').exists())
self.assertFalse((folder / 'autoconfig.php').exists())
if __name__ == '__main__':
unittest.main()