From 79e53f0d0093fecf65e170ac6bb8444be43773fd Mon Sep 17 00:00:00 2001 From: szaimen Date: Sun, 10 Jul 2022 21:47:25 +0200 Subject: [PATCH] allow to run daily backups without automatic updates Signed-off-by: szaimen --- Containers/mastercontainer/Dockerfile | 2 + .../backup-time-file-watcher.sh | 4 +- Containers/mastercontainer/cron.sh | 75 ++++-------------- Containers/mastercontainer/daily-backup.sh | 77 +++++++++++++++++++ Containers/nextcloud/entrypoint.sh | 2 +- manual-install/update-yaml.sh | 2 +- php/containers.json | 2 +- php/public/index.php | 3 +- .../Controller/ConfigurationController.php | 7 +- php/src/Controller/DockerController.php | 18 +++-- .../{DailyBackup.php => CreateBackup.php} | 3 - php/src/Cron/StartAndUpdateContainers.php | 17 ++++ php/src/Cron/StartContainers.php | 17 ++++ php/src/Cron/StopContainers.php | 17 ++++ php/src/Data/ConfigurationManager.php | 22 +++++- php/src/Docker/DockerActionManager.php | 4 +- php/templates/containers.twig | 20 +++-- 17 files changed, 203 insertions(+), 89 deletions(-) create mode 100644 Containers/mastercontainer/daily-backup.sh rename php/src/Cron/{DailyBackup.php => CreateBackup.php} (84%) create mode 100644 php/src/Cron/StartAndUpdateContainers.php create mode 100644 php/src/Cron/StartContainers.php create mode 100644 php/src/Cron/StopContainers.php diff --git a/Containers/mastercontainer/Dockerfile b/Containers/mastercontainer/Dockerfile index e35a0d49..352bb64f 100644 --- a/Containers/mastercontainer/Dockerfile +++ b/Containers/mastercontainer/Dockerfile @@ -88,11 +88,13 @@ COPY start.sh /usr/bin/ COPY backup-time-file-watcher.sh / COPY session-deduplicator.sh / COPY cron.sh / +COPY daily-backup.sh / COPY supervisord.conf / RUN chmod +x /usr/bin/start.sh; \ chmod +x /cron.sh; \ chmod +x /session-deduplicator.sh; \ chmod +x /backup-time-file-watcher.sh; \ + chmod +x /daily-backup.sh; \ chmod a+r /Caddyfile USER root diff --git a/Containers/mastercontainer/backup-time-file-watcher.sh b/Containers/mastercontainer/backup-time-file-watcher.sh index e7e34d16..ec9154ab 100644 --- a/Containers/mastercontainer/backup-time-file-watcher.sh +++ b/Containers/mastercontainer/backup-time-file-watcher.sh @@ -10,12 +10,12 @@ file_present() { if [ "$FILE_PRESENT" = 0 ]; then restart_process else - if [ -n "$BACKUP_TIME" ] && [ "$(cat "/mnt/docker-aio-config/data/daily_backup_time")" != "$BACKUP_TIME" ]; then + if [ -n "$BACKUP_TIME" ] && [ "$(head -1 "/mnt/docker-aio-config/data/daily_backup_time")" != "$BACKUP_TIME" ]; then restart_process fi fi FILE_PRESENT=1 - BACKUP_TIME="$(cat "/mnt/docker-aio-config/data/daily_backup_time")" + BACKUP_TIME="$(head -1 "/mnt/docker-aio-config/data/daily_backup_time")" else if [ "$FILE_PRESENT" = 1 ]; then restart_process diff --git a/Containers/mastercontainer/cron.sh b/Containers/mastercontainer/cron.sh index 8d0df12f..1fe5ff8d 100644 --- a/Containers/mastercontainer/cron.sh +++ b/Containers/mastercontainer/cron.sh @@ -3,18 +3,25 @@ while true; do if [ -f "/mnt/docker-aio-config/data/daily_backup_time" ]; then set -x - BACKUP_TIME="$(cat "/mnt/docker-aio-config/data/daily_backup_time")" - DAILY_BACKUP=1 + BACKUP_TIME="$(head -1 "/mnt/docker-aio-config/data/daily_backup_time")" + export BACKUP_TIME + export DAILY_BACKUP=1 + if [ "$(sed -n '2p' "/mnt/docker-aio-config/data/daily_backup_time")" != 'automaticUpdatesAreNotEnabled' ]; then + export AUTOMATIC_UPDATES=1 + else + export AUTOMATIC_UPDATES=0 + export START_CONTAINERS=1 + fi set +x else - BACKUP_TIME="04:00" - DAILY_BACKUP=0 + export BACKUP_TIME="04:00" + export DAILY_BACKUP=0 fi if [ -f "/mnt/docker-aio-config/data/daily_backup_running" ]; then - LOCK_FILE_PRESENT=1 + export LOCK_FILE_PRESENT=1 else - LOCK_FILE_PRESENT=0 + export LOCK_FILE_PRESENT=0 fi # Allow to continue directly if e.g. the mastercontainer was updated. Otherwise wait for the next execution @@ -25,61 +32,7 @@ while true; do fi if [ "$DAILY_BACKUP" = 1 ]; then - echo "Daily backup has started" - - # Delete all active sessions and create a lock file - # But don't kick out the user if the mastercontainer was just updated since we block the interface either way with the lock file - if [ "$LOCK_FILE_PRESENT" = 0 ]; then - rm -f "/mnt/docker-aio-config/session/"* - fi - sudo -u www-data touch "/mnt/docker-aio-config/data/daily_backup_running" - - # Check if apache is running/stopped, watchtower is stopped and backupcontainer is stopped - APACHE_PORT="$(docker inspect nextcloud-aio-apache --format "{{.HostConfig.PortBindings}}" | grep -oP '[0-9]+' | head -1)" - while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-apache$" && ! nc -z nextcloud-aio-apache "$APACHE_PORT"; do - echo "Waiting for apache to become available" - sleep 30 - done - while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-watchtower$"; do - echo "Waiting for watchtower to stop" - sleep 30 - done - while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-borgbackup$"; do - echo "Waiting for borgbackup to stop" - sleep 30 - done - - # Update the mastercontainer - sudo -u www-data php /var/www/docker-aio/php/src/Cron/UpdateMastercontainer.php - - # Wait for watchtower to stop - if ! docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-watchtower$"; then - echo "Something seems to be wrong: Watchtower should be started at this step." - else - while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-watchtower$"; do - echo "Waiting for watchtower to stop" - sleep 30 - done - fi - - # Execute the backup itself and some related tasks - sudo -u www-data php /var/www/docker-aio/php/src/Cron/DailyBackup.php - - # Delete the lock file - rm -f "/mnt/docker-aio-config/data/daily_backup_running" - - # Wait for the nextcloud container to start and send if the backup was successful - if ! docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-nextcloud$"; then - echo "Something seems to be wrong: Nextcloud should be started at this step." - else - while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-nextcloud$" && ! nc -z nextcloud-aio-nextcloud 9000; do - echo "Waiting for the Nextcloud container to start" - sleep 30 - done - fi - sudo -u www-data php /var/www/docker-aio/php/src/Cron/BackupNotification.php - - echo "Daily backup has finished" + bash /daily-backup.sh fi # Make sure to delete the lock file always diff --git a/Containers/mastercontainer/daily-backup.sh b/Containers/mastercontainer/daily-backup.sh new file mode 100644 index 00000000..ac6a4867 --- /dev/null +++ b/Containers/mastercontainer/daily-backup.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +echo "Daily backup has started" + +# Delete all active sessions and create a lock file +# But don't kick out the user if the mastercontainer was just updated since we block the interface either way with the lock file +if [ "$LOCK_FILE_PRESENT" = 0 ]; then + rm -f "/mnt/docker-aio-config/session/"* +fi +sudo -u www-data touch "/mnt/docker-aio-config/data/daily_backup_running" + +# Check if apache is running/stopped, watchtower is stopped and backupcontainer is stopped +APACHE_PORT="$(docker inspect nextcloud-aio-apache --format "{{.HostConfig.PortBindings}}" | grep -oP '[0-9]+' | head -1)" +while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-apache$" && ! nc -z nextcloud-aio-apache "$APACHE_PORT"; do + echo "Waiting for apache to become available" + sleep 30 +done +while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-watchtower$"; do + echo "Waiting for watchtower to stop" + sleep 30 +done +while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-borgbackup$"; do + echo "Waiting for borgbackup to stop" + sleep 30 +done + +# Update the mastercontainer +if [ "$AUTOMATIC_UPDATES" = 1 ]; then + sudo -u www-data php /var/www/docker-aio/php/src/Cron/UpdateMastercontainer.php +fi + +# Wait for watchtower to stop +if [ "$AUTOMATIC_UPDATES" = 1 ] && ! docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-watchtower$"; then + echo "Something seems to be wrong: Watchtower should be started at this step." +else + while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-watchtower$"; do + echo "Waiting for watchtower to stop" + sleep 30 + done +fi + +# Stop containers if required +if [ "$DAILY_BACKUP" != 1 ] || [ "$STOP_CONTAINERS" = 1 ]; then + sudo -u www-data php /var/www/docker-aio/php/src/Cron/StopContainers.php +fi + +# Execute the backup itself and some related tasks (also stops the containers) +if [ "$DAILY_BACKUP" = 1 ]; then + sudo -u www-data php /var/www/docker-aio/php/src/Cron/CreateBackup.php +fi + +# Start and/or update containers +if [ "$AUTOMATIC_UPDATES" = 1 ]; then + sudo -u www-data php /var/www/docker-aio/php/src/Cron/StartAndUpdateContainers.php +else + if [ "$START_CONTAINERS" = 1 ]; then + sudo -u www-data php /var/www/docker-aio/php/src/Cron/StartContainers.php + fi +fi + +# Delete the lock file +rm -f "/mnt/docker-aio-config/data/daily_backup_running" + +if [ "$DAILY_BACKUP" = 1 ]; then + # Wait for the nextcloud container to start and send if the backup was successful + if ! docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-nextcloud$"; then + echo "Something seems to be wrong: Nextcloud should be started at this step." + else + while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-nextcloud$" && ! nc -z nextcloud-aio-nextcloud 9000; do + echo "Waiting for the Nextcloud container to start" + sleep 30 + done + fi + sudo -u www-data php /var/www/docker-aio/php/src/Cron/BackupNotification.php +fi + +echo "Daily backup has finished" diff --git a/Containers/nextcloud/entrypoint.sh b/Containers/nextcloud/entrypoint.sh index d6a0ea58..2c4d8c0d 100644 --- a/Containers/nextcloud/entrypoint.sh +++ b/Containers/nextcloud/entrypoint.sh @@ -246,7 +246,7 @@ if ! [ -f "/mnt/ncdata/skip.update" ]; then fi # Performing update of all apps if daily backups are enabled, running and successful and if it is saturday - if [ "$DAILY_BACKUP_RUNNING" = 'yes' ] && [ "$(date +%u)" = 6 ]; then + if [ "$UPDATE_NEXTCLOUD_APPS" = 'yes' ] && [ "$(date +%u)" = 6 ]; then UPDATED_APPS="$(php /var/www/html/occ app:update --all)" if [ -n "$UPDATED_APPS" ]; then bash /notify.sh "Your apps just got updated!" "$UPDATED_APPS" diff --git a/manual-install/update-yaml.sh b/manual-install/update-yaml.sh index e30f937a..d12f3ed0 100644 --- a/manual-install/update-yaml.sh +++ b/manual-install/update-yaml.sh @@ -62,7 +62,7 @@ sed -i 's|COLLABORA_ENABLED=no|COLLABORA_ENABLED=yes|' sample.conf sed -i 's|COLLABORA_DICTIONARIES=|COLLABORA_DICTIONARIES=de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru # You can change this in order to enable other dictionaries for collabora|' sample.conf sed -i 's|NEXTCLOUD_DATADIR=|NEXTCLOUD_DATADIR=nextcloud_aio_nextcloud_data # You can change this to e.g. "/mnt/ncdata" to map it to a location on your host. It needs to be adjusted before the first startup and never afterwards!|' sample.conf sed -i 's|NEXTCLOUD_MOUNT=|NEXTCLOUD_MOUNT=/mnt/ # This allows the Nextcloud container to access directories on the host. It must never be equal to the value of NEXTCLOUD_DATADIR!|' sample.conf -sed -i 's|DAILY_BACKUP_RUNNING=|DAILY_BACKUP_RUNNING=no # When setting to yes, it will automatically update all installed Nextcloud apps upon container startup on saturdays.|' sample.conf +sed -i 's|UPDATE_NEXTCLOUD_APPS=|UPDATE_NEXTCLOUD_APPS=no # When setting to yes, it will automatically update all installed Nextcloud apps upon container startup.|' sample.conf sed -i 's|APACHE_PORT=|APACHE_PORT=443 # Changing this to a different value than 443 will allow you to run it behind a reverse proxy.|' sample.conf sed -i 's|TALK_PORT=|TALK_PORT=3478 # This allows to adjust the port that the talk container is using.|' sample.conf sed -i 's|AIO_TOKEN=|AIO_TOKEN=123456 # Has no function but needs to be set!|' sample.conf diff --git a/php/containers.json b/php/containers.json index 39c2fca1..4f7a84a6 100644 --- a/php/containers.json +++ b/php/containers.json @@ -138,7 +138,7 @@ "COLLABORA_HOST=nextcloud-aio-collabora", "TALK_ENABLED=%TALK_ENABLED%", "ONLYOFFICE_HOST=nextcloud-aio-onlyoffice", - "DAILY_BACKUP_RUNNING=%DAILY_BACKUP_RUNNING%", + "UPDATE_NEXTCLOUD_APPS=%UPDATE_NEXTCLOUD_APPS%", "TZ=%TIMEZONE%", "TALK_PORT=%TALK_PORT%" ], diff --git a/php/public/index.php b/php/public/index.php index abc1d17f..2a483da9 100644 --- a/php/public/index.php +++ b/php/public/index.php @@ -99,7 +99,8 @@ $app->get('/containers', function ($request, $response, $args) use ($container) 'timezone' => $configurationManager->GetTimezone(), 'skip_domain_validation' => $configurationManager->shouldDomainValidationBeSkipped(), 'talk_port' => $configurationManager->GetTalkPort(), - 'collabora_dictionaries' => $configurationManager->GetCollaboraDictionaries(), + 'collabora_dictionaries' => $configurationManager->GetCollaboraDictionaries(), + 'automatic_updates' => $configurationManager->areAutomaticUpdatesEnabled(), ]); })->setName('profile'); $app->get('/login', function ($request, $response, $args) use ($container) { diff --git a/php/src/Controller/ConfigurationController.php b/php/src/Controller/ConfigurationController.php index cf8c5031..109626e9 100644 --- a/php/src/Controller/ConfigurationController.php +++ b/php/src/Controller/ConfigurationController.php @@ -44,8 +44,13 @@ class ConfigurationController } if (isset($request->getParsedBody()['daily_backup_time'])) { + if (isset($request->getParsedBody()['automatic_updates'])) { + $enableAutomaticUpdates = true; + } else { + $enableAutomaticUpdates = false; + } $dailyBackupTime = $request->getParsedBody()['daily_backup_time'] ?? ''; - $this->configurationManager->SetDailyBackupTime($dailyBackupTime); + $this->configurationManager->SetDailyBackupTime($dailyBackupTime, $enableAutomaticUpdates); } if (isset($request->getParsedBody()['delete_daily_backup_time'])) { diff --git a/php/src/Controller/DockerController.php b/php/src/Controller/DockerController.php index dafe6748..9db7d627 100644 --- a/php/src/Controller/DockerController.php +++ b/php/src/Controller/DockerController.php @@ -26,22 +26,21 @@ class DockerController $this->configurationManager = $configurationManager; } - private function PerformRecursiveContainerStart(string $id) : void { + private function PerformRecursiveContainerStart(string $id, bool $pullContainer = true) : void { $container = $this->containerDefinitionFetcher->GetContainerById($id); foreach($container->GetDependsOn() as $dependency) { $this->PerformRecursiveContainerStart($dependency); } - $pullcontainer = true; if ($id === 'nextcloud-aio-database') { if ($this->dockerActionManager->GetDatabasecontainerExitCode() > 0) { - $pullcontainer = false; + $pullContainer = false; } } $this->dockerActionManager->DeleteContainer($container); $this->dockerActionManager->CreateVolumes($container); - if ($pullcontainer) { + if ($pullContainer) { $this->dockerActionManager->PullContainer($container); } else { error_log('Not pulling the latest database image because the container was not correctly shut down.'); @@ -145,12 +144,12 @@ class DockerController $this->configurationManager->WriteConfig($config); // Start container - $this->startTopContainer(); + $this->startTopContainer(true); return $response->withStatus(201)->withHeader('Location', '/'); } - public function startTopContainer() : void { + public function startTopContainer(bool $pullContainer) : void { $config = $this->configurationManager->GetConfig(); // set AIO_TOKEN $config['AIO_TOKEN'] = bin2hex(random_bytes(24)); @@ -161,7 +160,7 @@ class DockerController $id = self::TOP_CONTAINER; - $this->PerformRecursiveContainerStart($id); + $this->PerformRecursiveContainerStart($id, $pullContainer); } public function StartWatchtowerContainer(Request $request, Response $response, $args) : Response { @@ -195,6 +194,11 @@ class DockerController return $response->withStatus(201)->withHeader('Location', '/'); } + public function stopTopContainer() : void { + $id = self::TOP_CONTAINER; + $this->PerformRecursiveContainerStop($id); + } + public function StartDomaincheckContainer() : void { # Don't start if domain is already set diff --git a/php/src/Cron/DailyBackup.php b/php/src/Cron/CreateBackup.php similarity index 84% rename from php/src/Cron/DailyBackup.php rename to php/src/Cron/CreateBackup.php index 932bd04f..60366973 100644 --- a/php/src/Cron/DailyBackup.php +++ b/php/src/Cron/CreateBackup.php @@ -15,6 +15,3 @@ $dockerController = $container->get(\AIO\Controller\DockerController::class); // Stop container and start backup $dockerController->startBackup(); - -// Start apache -$dockerController->startTopContainer(); diff --git a/php/src/Cron/StartAndUpdateContainers.php b/php/src/Cron/StartAndUpdateContainers.php new file mode 100644 index 00000000..e72ae804 --- /dev/null +++ b/php/src/Cron/StartAndUpdateContainers.php @@ -0,0 +1,17 @@ +get(\AIO\Controller\DockerController::class); + +// Start apache +$dockerController->startTopContainer(true); diff --git a/php/src/Cron/StartContainers.php b/php/src/Cron/StartContainers.php new file mode 100644 index 00000000..98f9ae8f --- /dev/null +++ b/php/src/Cron/StartContainers.php @@ -0,0 +1,17 @@ +get(\AIO\Controller\DockerController::class); + +// Start apache +$dockerController->startTopContainer(false); diff --git a/php/src/Cron/StopContainers.php b/php/src/Cron/StopContainers.php new file mode 100644 index 00000000..89cf9798 --- /dev/null +++ b/php/src/Cron/StopContainers.php @@ -0,0 +1,17 @@ +get(\AIO\Controller\DockerController::class); + +// Start apache +$dockerController->stopTopContainer(); diff --git a/php/src/Data/ConfigurationManager.php b/php/src/Data/ConfigurationManager.php index d740a6c8..17f0b9cf 100644 --- a/php/src/Data/ConfigurationManager.php +++ b/php/src/Data/ConfigurationManager.php @@ -497,7 +497,7 @@ class ConfigurationManager /** * @throws InvalidSettingConfigurationException */ - public function SetDailyBackupTime(string $time) : void { + public function SetDailyBackupTime(string $time, bool $enableAutomaticUpdates) : void { if ($time === "") { throw new InvalidSettingConfigurationException("The daily backup time must not be empty!"); } @@ -506,6 +506,9 @@ class ConfigurationManager throw new InvalidSettingConfigurationException("You did not enter a correct time! One correct example is '04:00'!"); } + if ($enableAutomaticUpdates === false) { + $time .= '\nautomaticUpdatesAreNotEnabled'; + } file_put_contents(DataConst::GetDailyBackupTimeFile(), $time); } @@ -513,7 +516,22 @@ class ConfigurationManager if (!file_exists(DataConst::GetDailyBackupTimeFile())) { return ''; } - return file_get_contents(DataConst::GetDailyBackupTimeFile()); + $dailyBackupFile = file_get_contents(DataConst::GetDailyBackupTimeFile()); + $dailyBackupFileArray = explode("\n", $dailyBackupFile); + return $dailyBackupFileArray[0]; + } + + public function areAutomaticUpdatesEnabled() : bool { + if (!file_exists(DataConst::GetDailyBackupTimeFile())) { + return false; + } + $dailyBackupFile = file_get_contents(DataConst::GetDailyBackupTimeFile()); + $dailyBackupFileArray = explode("\n", $dailyBackupFile); + if (isset($dailyBackupFileArray[1]) && $dailyBackupFileArray[1] === 'automaticUpdatesAreNotEnabled') { + return false; + } else { + return true; + } } public function DeleteDailyBackupTime() : void { diff --git a/php/src/Docker/DockerActionManager.php b/php/src/Docker/DockerActionManager.php index ffd74ef7..8fb85fe3 100644 --- a/php/src/Docker/DockerActionManager.php +++ b/php/src/Docker/DockerActionManager.php @@ -280,8 +280,8 @@ class DockerActionManager } else { $replacements[1] = ''; } - } elseif ($out[1] === 'DAILY_BACKUP_RUNNING') { - if ($this->configurationManager->isDailyBackupRunning()) { + } elseif ($out[1] === 'UPDATE_NEXTCLOUD_APPS') { + if ($this->configurationManager->isDailyBackupRunning() && $this->configurationManager->areAutomaticUpdatesEnabled()) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; diff --git a/php/templates/containers.twig b/php/templates/containers.twig index a11a6cd7..d5bc0cef 100644 --- a/php/templates/containers.twig +++ b/php/templates/containers.twig @@ -51,13 +51,15 @@ {% if is_daily_backup_running == true %} Daily backup currently running. (Logs)

- It will update your containers, the mastercontainer and on saturdays your Nextcloud apps if the backup is successful.

- {% if is_mastercontainer_update_available == true %} - Since the mastercontainer gets updated, it will restart the container which will make it unavailable for a moment. (Logs)

+ {% if automatic_updates == true %} + It will update your containers, the mastercontainer and on saturdays your Nextcloud apps if the backup is successful.

+ {% if is_mastercontainer_update_available == true %} + Since the mastercontainer gets updated, it will restart the container which will make it unavailable for a moment. (Logs)

+ {% endif %} {% endif %} {% if has_update_available == false %} The whole process should not take more than a few minutes.

- {% else %} + {% elseif automatic_updates == true %} The whole process can take a while because your containers get updated.

{% endif %} Reload ↻
@@ -380,11 +382,15 @@ - +
+
- This option will also automatically update your containers, the mastercontainer and on saturdays your Nextcloud apps and will send a notification about the result of the backup.

{% else %} - Daily backups will be created at {{ daily_backup_time }} UTC which includes a notification about the result of the backup and automatic updates of your containers, the mastercontainer and on saturdays your Nextcloud apps. You can disable this option again by clicking on the button below.

+ Daily backups will be created at {{ daily_backup_time }} UTC which includes a notification about the result of the backup. + {% if automatic_updates == true %} + Also your containers, the mastercontainer and on saturdays your Nextcloud apps will be automatically updated. + {% endif %} + You can disable this option again by clicking on the button below.