Compare commits

...

26 Commits

Author SHA1 Message Date
Simon L
9d9da9e47c Merge pull request #230 from nextcloud/enh/noid/update-version
update version to 0.4.0
2022-02-16 16:30:44 +01:00
szaimen
3a9c305758 update version to 0.4.0
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-16 16:29:47 +01:00
szaimen
8f1fcde006 fix selecting a backup container
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-16 16:26:35 +01:00
Simon L
8a6de8f63c Merge pull request #65 from nextcloud/enh/39/allow-to-select-archive
allow to select the archive to restore from
2022-02-16 14:43:10 +01:00
szaimen
0880aff7ea adress review
Signed-off-by: szaimen <szaimen@e.mail.de>
Co-Authored-By: Carl Schwan <carl@carlschwan.eu>
2022-02-16 14:34:21 +01:00
Simon L
2c3db2fd40 Merge pull request #229 from nextcloud/nextcloud-container-update
Nextcloud update
2022-02-16 13:39:37 +01:00
szaimen
e357fc8bb9 nextcloud-update automated change
Signed-off-by: GitHub <noreply@github.com>
2022-02-16 12:33:48 +00:00
Simon L
a82b86b915 Merge pull request #222 from nextcloud/aio-dependency-update
Dependency updates
2022-02-16 13:28:10 +01:00
szaimen
67705134cf dependency updates
Signed-off-by: GitHub <noreply@github.com>
2022-02-16 12:13:04 +00:00
Simon L
ce15c64cd9 Merge pull request #228 from nextcloud/enh/noid/update-nextcloud
update nextcloud to 23.0.2
2022-02-16 12:19:41 +01:00
szaimen
92492affe4 update nextcloud to 23.0.2
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-16 12:18:46 +01:00
szaimen
63e0849215 allow to select the archive to restore from
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-16 12:00:54 +01:00
szaimen
1515d0c210 another attempt to fix the database import
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-15 18:57:39 +01:00
Simon L
4359792a48 Merge pull request #226 from nextcloud/enh/noid/fix-database-import
fix database import
2022-02-15 17:44:36 +01:00
szaimen
3887275808 fix database import
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-15 17:44:00 +01:00
szaimen
badd8d02a9 update to 0.3.1
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-15 12:56:44 +01:00
Simon L
5bc10a56ed Merge pull request #218 from nextcloud/enh/noid/fix-database-connection
fix the database connection
2022-02-15 12:15:39 +01:00
Simon L
222dcd3317 Merge pull request #221 from nextcloud/aio-dependency-update
Dependency updates
2022-02-13 13:43:13 +01:00
szaimen
cec86aa825 dependency updates
Signed-off-by: GitHub <noreply@github.com>
2022-02-13 12:11:36 +00:00
szaimen
e7dfb2514b fix the database connection
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-13 11:44:59 +01:00
szaimen
b0e570176b fix redis build
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-12 14:24:11 +01:00
szaimen
7756826eb3 revert the removal for now since it would break all instances
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-12 01:02:39 +01:00
szaimen
836552e1a7 verbose output
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-12 00:26:12 +01:00
szaimen
6e22b364ae postgresql - delete the datadir once
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-11 23:53:50 +01:00
Simon L
4979d1d90e Merge pull request #214 from nextcloud/enh/131/use-dependabot-to-update-containers
use dependabot to update containers
2022-02-11 18:42:45 +01:00
szaimen
3029b277f1 use dependabot to update containers
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-02-11 15:47:49 +01:00
23 changed files with 163 additions and 59 deletions

View File

@@ -3,10 +3,17 @@ updates:
- package-ecosystem: composer
directory: "/php/"
schedule:
interval: weekly
day: saturday
time: "03:00"
timezone: Europe/Paris
interval: daily
time: "12:00"
open-pull-requests-limit: 10
labels:
- 3. to review
- dependencies
- package-ecosystem: "docker"
directory: "/Containers/"
schedule:
interval: "daily"
time: "12:00"
open-pull-requests-limit: 10
labels:
- 3. to review

View File

@@ -1,4 +1,4 @@
FROM debian:bullseye-slim
FROM debian:bullseye-20220125-slim
EXPOSE 80

View File

@@ -1,4 +1,4 @@
FROM debian:bullseye-slim
FROM debian:bullseye-20220125-slim
RUN set -ex; \
\

View File

@@ -1,13 +1,9 @@
#!/bin/bash
# Variables
BORG_BACKUP_DIRECTORY="/mnt/borgbackup/borg"
# Functions
get_start_time(){
START_TIME=$(date +%s)
CURRENT_DATE=$(date --date @"$START_TIME" +"%Y%m%d_%H%M%S")
CURRENT_DATE_READABLE=$(date --date @"$START_TIME" +"%d.%m.%Y - %H:%M:%S")
}
get_expiration_time() {
END_TIME=$(date +%s)
@@ -19,11 +15,6 @@ get_expiration_time() {
DURATION_READABLE=$(printf "%02d hours %02d minutes %02d seconds" $DURATION_HOUR $DURATION_MIN $DURATION_SEC)
}
# Export defaults
export BORG_PASSPHRASE="$BORG_PASSWORD"
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
# Test if all volumes aren't empty
VOLUME_DIRS="$(find /nextcloud_aio_volumes -mindepth 1 -maxdepth 1 -type d)"
mapfile -t VOLUME_DIRS <<< "$VOLUME_DIRS"
@@ -150,9 +141,6 @@ if [ "$BORG_MODE" = backup ]; then
# Remove the update skip file because the backup was successful
rm -f "/nextcloud_aio_volumes/nextcloud_aio_nextcloud_data/skip.update"
echo "$CURRENT_DATE,$CURRENT_DATE_READABLE" >> "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
chmod +r "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
# Prune options
BORG_PRUNE_OPTS=(--stats --progress --keep-within=7d --keep-weekly=4 --keep-monthly=6 "$BORG_BACKUP_DIRECTORY")
@@ -172,17 +160,20 @@ fi
# Do the restore
if [ "$BORG_MODE" = restore ]; then
get_start_time
echo "Restoring the last backup..."
# Perform the restore
FIRST_ARCHIVE="$(borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1}' | sort -r | head -1)"
if [ -n "$SELECTED_RESTORE_TIME" ]; then
SELECTED_ARCHIVE="$(borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | grep "$SELECTED_RESTORE_TIME" | awk -F " " '{print $1}' | head -1)"
else
SELECTED_ARCHIVE="$(borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1}' | sort -r | head -1)"
fi
echo "Restoring '$SELECTED_ARCHIVE'..."
mkdir -p /tmp/borg
if ! borg mount "$BORG_BACKUP_DIRECTORY::$FIRST_ARCHIVE" /tmp/borg; then
if ! borg mount "$BORG_BACKUP_DIRECTORY::$SELECTED_ARCHIVE" /tmp/borg; then
echo "Could not mount the backup!"
exit 1
fi
if ! rsync --stats --archive --human-readable -vv --delete \
--exclude "nextcloud_aio_mastercontainer/data/backup_archives.list" \
--exclude "nextcloud_aio_mastercontainer/session/"** \
--exclude "nextcloud_aio_mastercontainer/certs/"** \
/tmp/borg/nextcloud_aio_volumes/ /nextcloud_aio_volumes; then

View File

@@ -1,12 +1,18 @@
#!/bin/bash
# Variables
export BORG_BACKUP_DIRECTORY="/mnt/borgbackup/borg"
# Validate BORG_PASSWORD
if [ -z "$BORG_PASSWORD" ]; then
echo "BORG_PASSWORD is not allowed to be empty."
exit 1
fi
export BORG_PASSWORD
# Export defaults
export BORG_PASSPHRASE="$BORG_PASSWORD"
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
# Validate BORG_MODE
if [ "$BORG_MODE" != backup ] && [ "$BORG_MODE" != restore ] && [ "$BORG_MODE" != check ]; then
@@ -24,6 +30,12 @@ fi
# Remove lockfile
rm -f "/nextcloud_aio_volumes/nextcloud_aio_database_dump/backup-is-running"
# Get a list of all available borg archives
set -x
borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1","$3,$4}' > "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
chmod +r "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
set +x
if [ -n "$FAILED" ]; then
if [ "$BORG_MODE" = backup ]; then
# Add file to Nextcloud container so that it skips any update the next time

View File

@@ -1,2 +1,2 @@
# From a file located probably somewhere here: https://github.com/CollaboraOnline/online/tree/master/docker
FROM collabora/code:latest
FROM collabora/code:21.11.1.4.1

View File

@@ -1,4 +1,4 @@
FROM alpine
FROM alpine:3.15.0
RUN apk add --update --no-cache lighttpd bash
RUN adduser -S www-data -G www-data

View File

@@ -1,5 +1,8 @@
# Docker CLI is a requirement
FROM docker:20.10.12-dind-alpine3.15 as dind
# From https://github.com/docker-library/php/blob/master/8.0/bullseye/apache/Dockerfile
FROM php:8.0-apache-bullseye
FROM php:8.0.15-apache-bullseye
EXPOSE 80
EXPOSE 8080
@@ -28,7 +31,7 @@ RUN set -ex; \
&& chmod +x /usr/bin/caddy \
&& /usr/bin/caddy version
COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/
COPY --from=dind /usr/local/bin/docker /usr/local/bin/
RUN chmod +x /usr/local/bin/docker
RUN mkdir -p /usr/src/php/ext/apcu && \

View File

@@ -1,5 +1,5 @@
# From https://github.com/nextcloud/docker/blob/master/23/fpm-alpine/Dockerfile
FROM php:8.0-fpm-alpine
FROM php:8.0.15-fpm-alpine3.15
# Custom: change id of www-data user as it needs to be the same like on old installations
RUN set -ex; \
@@ -62,7 +62,7 @@ RUN set -ex; \
# pecl will claim success even if one install fails, so we need to perform each install separately
pecl install APCu-5.1.21; \
pecl install memcached-3.1.5; \
pecl install redis-5.3.6; \
pecl install redis-5.3.7; \
pecl install imagick-3.7.0; \
\
docker-php-ext-enable \
@@ -105,7 +105,7 @@ RUN { \
VOLUME /var/www/html
ENV NEXTCLOUD_VERSION 23.0.1
ENV NEXTCLOUD_VERSION 23.0.2
RUN set -ex; \
apk add --no-cache --virtual .fetch-deps \

View File

@@ -6,6 +6,16 @@ while ! nc -z "$POSTGRES_HOST" 5432; do
sleep 5
done
# Use the correct Postgres username
POSTGRES_USER="oc_$POSTGRES_USER"
export POSTGRES_USER
# Fix false database connection on old instances
if [ -f "/var/www/html/config/config.php" ] && sleep 2 && psql -d "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:5432/$POSTGRES_DB" -c "select now()"; then
sed -i "s|'dbuser'.*=>.*$|'dbuser' => '$POSTGRES_USER',|" /var/www/html/config/config.php
sed -i "s|'dbpassword'.*=>.*$|'dbpassword' => '$POSTGRES_PASSWORD',|" /var/www/html/config/config.php
fi
# Run original entrypoint
if ! bash /entrypoint.sh; then
exit 1

View File

@@ -1,5 +1,5 @@
# From https://github.com/docker-library/postgres/blob/master/13/alpine/Dockerfile
FROM postgres:13-alpine
FROM postgres:13.5-alpine3.15
RUN apk add --update --no-cache bash openssl shadow
@@ -17,7 +17,9 @@ RUN set -ex; \
chown -R postgres:postgres "$PGDATA"
COPY start.sh /usr/bin/
RUN chmod +x /usr/bin/start.sh
COPY init-user-db.sh /docker-entrypoint-initdb.d/
RUN chmod +x /usr/bin/start.sh; \
chmod +xr /docker-entrypoint-initdb.d/init-user-db.sh
RUN mkdir /mnt/data; \
chown postgres:postgres /mnt/data;

View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -ex
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE USER "oc_$POSTGRES_USER" WITH PASSWORD '$POSTGRES_PASSWORD' CREATEDB;
ALTER DATABASE "$POSTGRES_DB" OWNER TO "oc_$POSTGRES_USER";
EOSQL
set +ex

View File

@@ -18,10 +18,18 @@ if ! [ -w "$DUMP_DIR" ]; then
exit 1
fi
# Delete the datadir once (needed for setting the correct credentials on old instances once)
if ! [ -f "$DUMP_DIR/export.failed" ] && ! [ -f "$DUMP_DIR/initial-cleanup-done" ]; then
set -ex
rm -rf "${DATADIR:?}/"*
touch "$DUMP_DIR/initial-cleanup-done"
set +ex
fi
# Test if some things match
# shellcheck disable=SC2235
if ( [ -f "$DATADIR/PG_VERSION" ] && [ "$PG_MAJOR" != "$(cat "$DATADIR/PG_VERSION")" ] ) \
|| ( ! [ -f "$DATADIR/PG_VERSION" ] && [ -f "$DUMP_FILE" ] ); then
|| ( ! [ -f "$DATADIR/PG_VERSION" ] && ( [ -f "$DUMP_FILE" ] || [ -f "$DUMP_DIR/export.failed" ] ) ); then
# The DUMP_file must be provided
if ! [ -f "$DUMP_FILE" ]; then
echo "Unable to restore the database because the database dump is missing."
@@ -39,7 +47,7 @@ if ( [ -f "$DATADIR/PG_VERSION" ] && [ "$PG_MAJOR" != "$(cat "$DATADIR/PG_VERSIO
echo "Restoring from database dump."
# Exit if any command fails
set -e
set -ex
# Remove old database files
rm -rf "${DATADIR:?}/"*
@@ -50,13 +58,30 @@ if ( [ -f "$DATADIR/PG_VERSION" ] && [ "$PG_MAJOR" != "$(cat "$DATADIR/PG_VERSIO
# Create new database
exec docker-entrypoint.sh postgres &
# Wait 2s for creation
sleep 2s
# Wait 10s for creation
sleep 10s
# Set correct permissions
if grep -q "Owner: oc_admin" "$DUMP_FILE" && ! grep -q "Owner: oc_$POSTGRES_USER" "$DUMP_FILE"; then
OC_ADMIN_EXISTS=1
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE USER oc_admin WITH PASSWORD '$POSTGRES_PASSWORD' CREATEDB;
ALTER DATABASE "$POSTGRES_DB" OWNER TO oc_admin;
EOSQL
fi
# Restore database
echo "Restoring the database from database dump"
psql "$POSTGRES_DB" -U "$POSTGRES_USER" < "$DUMP_FILE"
# Correct permissions
if [ -n "$OC_ADMIN_EXISTS" ]; then
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
ALTER DATABASE "$POSTGRES_DB" OWNER TO "oc_$POSTGRES_USER";
REASSIGN OWNED BY oc_admin TO "oc_$POSTGRES_USER";
EOSQL
fi
# Shut down the database to be able to start it again
pg_ctl stop -m fast
@@ -64,7 +89,7 @@ if ( [ -f "$DATADIR/PG_VERSION" ] && [ "$PG_MAJOR" != "$(cat "$DATADIR/PG_VERSIO
export PGPORT=5432
# Don't exit if command fails anymore
set +e
set +ex
fi
# Cover the last case

View File

@@ -1,5 +1,5 @@
# From https://github.com/docker-library/redis/blob/master/6.2/alpine/Dockerfile
FROM redis:6.2-alpine
FROM redis:6.2.6-alpine3.15
RUN apk add --update --no-cache openssl bash

View File

@@ -1,4 +1,4 @@
FROM ubuntu:focal
FROM ubuntu:focal-20220113
EXPOSE 3478

View File

@@ -1,7 +1,7 @@
# From https://github.com/containrrr/watchtower/blob/main/dockerfiles/Dockerfile.self-contained
FROM containrrr/watchtower:latest as watchtower
FROM containrrr/watchtower:1.4.0 as watchtower
FROM alpine:latest
FROM alpine:3.15.0
RUN apk add --update --no-cache bash
COPY --from=watchtower /watchtower /

24
php/composer.lock generated
View File

@@ -50,12 +50,12 @@
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\": "src/"
},
"files": [
"src/functions_include.php"
]
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -595,12 +595,12 @@
},
"type": "library",
"autoload": {
"psr-4": {
"DI\\": "src/"
},
"files": [
"src/functions.php"
]
],
"psr-4": {
"DI\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1551,12 +1551,12 @@
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [

View File

@@ -194,7 +194,8 @@
"internalPorts": [],
"environmentVariables": [
"BORG_PASSWORD=%BORGBACKUP_PASSWORD%",
"BORG_MODE=%BORGBACKUP_MODE%"
"BORG_MODE=%BORGBACKUP_MODE%",
"SELECTED_RESTORE_TIME=%SELECTED_RESTORE_TIME%"
],
"volumes": [
{

View File

@@ -87,6 +87,7 @@ $app->get('/containers', function ($request, $response, $args) use ($container)
'was_start_button_clicked' => $configurationManager->wasStartButtonClicked(),
'has_update_available' => $dockerActionManger->isAnyUpdateAvailable(),
'last_backup_time' => $configurationManager->GetLastBackupTime(),
'backup_times' => $configurationManager->GetBackupTimes(),
]);
})->setName('profile');
$app->get('/login', function ($request, $response, $args) use ($container) {

View File

@@ -83,6 +83,7 @@ class DockerController
public function StartBackupContainerRestore(Request $request, Response $response, $args) : Response {
$config = $this->configurationManager->GetConfig();
$config['backup-mode'] = 'restore';
$config['selected-restore-time'] = $request->getParsedBody()['selected_restore_time'];
$this->configurationManager->WriteConfig($config);
$id = self::TOP_CONTAINER;

View File

@@ -64,7 +64,7 @@ class ConfigurationManager
}
$content = file_get_contents(DataConst::GetBackupArchivesList());
if ($content === "") {
if ($content === '') {
return '';
}
@@ -83,6 +83,32 @@ class ConfigurationManager
return $lastBackupTime;
}
public function GetBackupTimes() : array {
if (!file_exists(DataConst::GetBackupArchivesList())) {
return [];
}
$content = file_get_contents(DataConst::GetBackupArchivesList());
if ($content === '') {
return [];
}
$backupLines = explode("\n", $content);
$backupTimes = [];
foreach($backupLines as $lines) {
if ($lines !== "") {
$backupTimesTemp = explode(',', $lines);
$backupTimes[] = $backupTimesTemp[1];
}
}
if (!is_array($backupTimes)) {
return [];
}
return $backupTimes;
}
public function wasStartButtonClicked() : bool {
if (isset($this->GetConfig()['wasStartButtonClicked'])) {
return true;
@@ -152,6 +178,15 @@ class ConfigurationManager
return $config['backup-mode'];
}
public function GetSelectedRestoreTime() : string {
$config = $this->GetConfig();
if(!isset($config['selected-restore-time'])) {
$config['selected-restore-time'] = '';
}
return $config['selected-restore-time'];
}
public function GetAIOURL() : string {
$config = $this->GetConfig();
if(!isset($config['AIO_URL'])) {

View File

@@ -212,6 +212,8 @@ class DockerActionManager
$replacements[1] = $this->configurationManager->GetBackupMode();
} elseif ($out[1] === 'AIO_URL') {
$replacements[1] = $this->configurationManager->GetAIOURL();
} elseif ($out[1] === 'SELECTED_RESTORE_TIME') {
$replacements[1] = $this->configurationManager->GetSelectedRestoreTime();
} else {
$replacements[1] = $this->configurationManager->GetSecret($out[1]);
}

View File

@@ -16,7 +16,7 @@
</header>
<div class="content">
<h1>Nextcloud AIO Beta v0.3.0</h1>
<h1>Nextcloud AIO Beta v0.4.0</h1>
This is beta software and not production ready.<br><br>
{% set isAnyRunning = false %}
@@ -206,11 +206,16 @@
<input class="button" type="submit" value="Check backup integrity" onclick="return confirm('Check backup integrity? Are you sure that you want to check the backup? This can take a long time depending on the size of your backup.')" /><br/>
</form>
Click on the button below to restore the last backup from {{ last_backup_time }}. This will overwrite all your files with the state of the backup. It makes sense to run an integrity check before restoring your files but is not mandatory since it shouldn't be needed in most situations.<br><br>
<form method="POST" action="/api/docker/restore" class="xhr">
Choose the backup that you want to restore and click on the button below to restore the selected backup. This will overwrite all your files with the state of the backup so you should consider creating a backup first. It also makes sense to run an integrity check before restoring your files but is not mandatory since it shouldn't be needed in most situations.<br><br>
<form method="POST" action="/api/docker/restore" class="xhr" id="restore_selection">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input class="button" type="submit" value="Restore last backup" onclick="return confirm('Restore last backup? Are you sure that you want to restore the last backup? This will stop all running containers and restore the last backup from {{ last_backup_time }}. You might want to check the backup integrity first.')" />
<select id="selected_restore_time" name="selected_restore_time" form="restore_selection">
{% for restore_time in backup_times %}
<option value="{{ restore_time }}">{{ restore_time }}</option>
{% endfor %}
</select>
<input class="button" type="submit" value="Restore selected backup" onclick="return confirm('Restore the selected backup? Are you sure that you want to restore the selected backup? This will stop all running containers and restore the selected backup. It is recommended to create a backup first. You might also want to check the backup integrity.')" />
</form>
{% endif %}
{% endif %}