Full restore: a run again after an interrupted restore keeps maintenance mode on when its check refuses; retry text about apps
This commit is contained in:
@@ -101,8 +101,9 @@ mode on yourself before, it stays on: switch it off with `occ maintenance:mode -
|
|||||||
If it stops halfway, maintenance mode stays on (the instance may be partly restored). Fix the cause,
|
If it stops halfway, maintenance mode stays on (the instance may be partly restored). Fix the cause,
|
||||||
then switch maintenance mode off and run the restore again with `--force`, in one line (the message
|
then switch maintenance mode off and run the restore again with `--force`, in one line (the message
|
||||||
prints it): `occ maintenance:mode --off && occ backup:restore:full <location> <snapshot> --force`.
|
prints it): `occ maintenance:mode --off && occ backup:restore:full <location> <snapshot> --force`.
|
||||||
Leave out `--install-apps` this time: the apps are installed already, and the instance is then open
|
Leave out `--install-apps` this time, so that the instance is open only for the seconds until the
|
||||||
only for the seconds until the restore has switched maintenance mode on again.
|
restore has switched maintenance mode on again; apps the first run could not install are listed at
|
||||||
|
the end of the restore, install those afterwards.
|
||||||
If occ then says that Nextcloud or one of the apps requires an upgrade, the database is already
|
If occ then says that Nextcloud or one of the apps requires an upgrade, the database is already
|
||||||
the old server's: run `occ upgrade` first, then the same command with `--force`.
|
the old server's: run `occ upgrade` first, then the same command with `--force`.
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ HELP);
|
|||||||
$again = 'occ backup:restore:full ' . escapeshellarg((string)$input->getArgument('target')) . ' ' . escapeshellarg((string)$input->getArgument('snapshot'))
|
$again = 'occ backup:restore:full ' . escapeshellarg((string)$input->getArgument('target')) . ' ' . escapeshellarg((string)$input->getArgument('snapshot'))
|
||||||
. ($input->getOption('skip-missing-tables') ? ' --skip-missing-tables' : '') . ' --force --no-interaction';
|
. ($input->getOption('skip-missing-tables') ? ' --skip-missing-tables' : '') . ' --force --no-interaction';
|
||||||
$output->writeln(' occ maintenance:mode --off && ' . $again);
|
$output->writeln(' occ maintenance:mode --off && ' . $again);
|
||||||
$output->writeln('(Without --install-apps: the apps are installed already, and the instance is open again only for the seconds until the restore starts.)');
|
$output->writeln('(Without --install-apps, so the instance is open only for the seconds until the restore starts again. Apps the first run could not install are listed at the end: install those afterwards.)');
|
||||||
} else {
|
} else {
|
||||||
$output->writeln('If this happened after the restore had started, the instance may be partly restored: fix the cause and run the same command again with --force.');
|
$output->writeln('If this happened after the restore had started, the instance may be partly restored: fix the cause and run the same command again with --force.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ final class RestoreFullService {
|
|||||||
* Config keys that must keep pointing at THIS installation's actual, already-working
|
* Config keys that must keep pointing at THIS installation's actual, already-working
|
||||||
* database and data directory (set by occ maintenance:install), never at the old server's.
|
* database and data directory (set by occ maintenance:install), never at the old server's.
|
||||||
*/
|
*/
|
||||||
|
/** App setting: when a full restore started that has not completed (see restoreFull()). */
|
||||||
|
private const INTERRUPTED_KEY = 'restore_full_started';
|
||||||
|
|
||||||
private const CONNECTION_KEYS = [
|
private const CONNECTION_KEYS = [
|
||||||
'dbtype', 'dbhost', 'dbport', 'dbname', 'dbuser', 'dbpassword', 'dbpersistent',
|
'dbtype', 'dbhost', 'dbport', 'dbname', 'dbuser', 'dbpassword', 'dbpersistent',
|
||||||
'dbtableprefix', 'dbdriveroptions', 'datadirectory',
|
'dbtableprefix', 'dbdriveroptions', 'datadirectory',
|
||||||
@@ -98,23 +101,31 @@ final class RestoreFullService {
|
|||||||
// right before running the restore again (see RestoreFull). Already on before: left on.
|
// right before running the restore again (see RestoreFull). Already on before: left on.
|
||||||
// On before the checks below, so that a run again after an error leaves the partly
|
// On before the checks below, so that a run again after an error leaves the partly
|
||||||
// restored instance open for seconds at most; off again when they refuse (nothing changed).
|
// restored instance open for seconds at most; off again when they refuse (nothing changed).
|
||||||
|
// A marker in ng_backup's own app settings (which the restore keeps) says a restore started
|
||||||
|
// and did not complete: then the instance may be partly restored, and a check that refuses
|
||||||
|
// a run again must not open it.
|
||||||
|
$appConfig = \OCP\Server::get(\OCP\IAppConfig::class);
|
||||||
|
$interrupted = $appConfig->getValueInt(\OCA\NgBackup\AppInfo\Application::APP_ID, self::INTERRUPTED_KEY, 0) > 0;
|
||||||
$wasInMaintenance = $this->config->getSystemValueBool('maintenance', false);
|
$wasInMaintenance = $this->config->getSystemValueBool('maintenance', false);
|
||||||
$this->config->setSystemValue('maintenance', true);
|
$this->config->setSystemValue('maintenance', true);
|
||||||
|
$reopen = function () use ($wasInMaintenance, $interrupted): void {
|
||||||
|
if (!$wasInMaintenance && !$interrupted) {
|
||||||
|
$this->config->setSystemValue('maintenance', false);
|
||||||
|
}
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
$check = $this->checkCompatibility($target, $snapshotId, $skipMissingTables);
|
$check = $this->checkCompatibility($target, $snapshotId, $skipMissingTables);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
if (!$wasInMaintenance) {
|
$reopen();
|
||||||
$this->config->setSystemValue('maintenance', false);
|
|
||||||
}
|
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
if (!$check['ok']) {
|
if (!$check['ok']) {
|
||||||
if (!$wasInMaintenance) {
|
$reopen();
|
||||||
$this->config->setSystemValue('maintenance', false);
|
|
||||||
}
|
|
||||||
throw new \RuntimeException($this->describeBlockers($check));
|
throw new \RuntimeException($this->describeBlockers($check));
|
||||||
}
|
}
|
||||||
|
$appConfig->setValueInt(\OCA\NgBackup\AppInfo\Application::APP_ID, self::INTERRUPTED_KEY, time());
|
||||||
$result = $this->restoreInMaintenance($target, $snapshotId, $skipMissingTables, $check);
|
$result = $this->restoreInMaintenance($target, $snapshotId, $skipMissingTables, $check);
|
||||||
|
$appConfig->deleteKey(\OCA\NgBackup\AppInfo\Application::APP_ID, self::INTERRUPTED_KEY);
|
||||||
if (!$wasInMaintenance) {
|
if (!$wasInMaintenance) {
|
||||||
$this->config->setSystemValue('maintenance', false);
|
$this->config->setSystemValue('maintenance', false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ OUT=$(run_on ngb-frt-restore backup:restore:full offsite "$SNAP" "${RESTORE_OPTS
|
|||||||
}
|
}
|
||||||
printf '%s\n' "$OUT" | grep -q "maintenance mode while the restore runs" || fail "the restore did not say it runs in maintenance mode"
|
printf '%s\n' "$OUT" | grep -q "maintenance mode while the restore runs" || fail "the restore did not say it runs in maintenance mode"
|
||||||
run_on ngb-frt-restore status 2>&1 | grep -q "maintenance: false" || fail "maintenance mode is still on after the restore"
|
run_on ngb-frt-restore status 2>&1 | grep -q "maintenance: false" || fail "maintenance mode is still on after the restore"
|
||||||
|
run_on ngb-frt-restore config:app:get ng_backup restore_full_started >/dev/null 2>&1 && fail "the restore left its 'started' marker behind"
|
||||||
ok "backup:restore:full completed, in maintenance mode and out of it again"
|
ok "backup:restore:full completed, in maintenance mode and out of it again"
|
||||||
|
|
||||||
# Everything below must work in a FRESH occ process (a new PHP process per docker exec), since
|
# Everything below must work in a FRESH occ process (a new PHP process per docker exec), since
|
||||||
|
|||||||
Reference in New Issue
Block a user