Skip to content
35 changes: 2 additions & 33 deletions Containers/nextcloud/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -401,41 +401,10 @@ EOF

# AIO update to latest start # Do not remove or change this line!
if [ "$INSTALL_LATEST_MAJOR" = yes ]; then
php /var/www/html/occ config:system:set updatedirectory --value="/nc-updater"
INSTALLED_AT="$(php /var/www/html/occ config:app:get core installedat)"
if [ -n "${INSTALLED_AT}" ]; then
# Set the installdat to 00 which will allow to skip staging and install the next major directly
# shellcheck disable=SC2001
INSTALLED_AT="$(echo "${INSTALLED_AT}" | sed "s|[0-9][0-9]$|00|")"
php /var/www/html/occ config:app:set core installedat --value="${INSTALLED_AT}"
fi
php /var/www/html/updater/updater.phar --no-interaction --no-backup
if ! php /var/www/html/occ -V || php /var/www/html/occ status | grep maintenance | grep -q 'true'; then
echo "Installation of Nextcloud failed!"
touch "$NEXTCLOUD_DATA_DIR/install.failed"
if ! bash /upgrade-latest-major.sh; then
echo "Upgrade to latest major version failed! Check the output above for details."
exit 1
fi
# shellcheck disable=SC2016
installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
INSTALLED_MAJOR="${installed_version%%.*}"
IMAGE_MAJOR="${image_version%%.*}"
# If a valid upgrade path, trigger the Nextcloud built-in Updater
if ! [ "$INSTALLED_MAJOR" -gt "$IMAGE_MAJOR" ]; then
php /var/www/html/updater/updater.phar --no-interaction --no-backup
if ! php /var/www/html/occ -V || php /var/www/html/occ status | grep maintenance | grep -q 'true'; then
echo "Installation of Nextcloud failed!"
# TODO: Add a hint here about what to do / where to look / updater.log?
touch "$NEXTCLOUD_DATA_DIR/install.failed"
exit 1
fi
# shellcheck disable=SC2016
installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
fi
php /var/www/html/occ config:system:set updatechecker --type=bool --value=true
php /var/www/html/occ app:enable nextcloud-aio --force
php /var/www/html/occ db:add-missing-columns
php /var/www/html/occ db:add-missing-primary-keys
yes | php /var/www/html/occ db:convert-filecache-bigint
fi
# AIO update to latest end # Do not remove or change this line!

Expand Down
40 changes: 40 additions & 0 deletions Containers/nextcloud/upgrade-latest-major.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash

# shellcheck disable=SC2016
image_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
IMAGE_MAJOR="${image_version%%.*}"

php /var/www/html/occ config:system:set updatedirectory --value="/nc-updater"
INSTALLED_AT="$(php /var/www/html/occ config:app:get core installedat)"
if [ -n "${INSTALLED_AT}" ]; then
# Set the installedat to 00 which will allow to skip staging and install the next major directly
# shellcheck disable=SC2001
INSTALLED_AT="$(echo "${INSTALLED_AT}" | sed "s|[0-9][0-9]$|00|")"
php /var/www/html/occ config:app:set core installedat --value="${INSTALLED_AT}"
fi
php /var/www/html/updater/updater.phar --no-interaction --no-backup
if ! php /var/www/html/occ -V || php /var/www/html/occ status | grep maintenance | grep -q 'true'; then
echo "Installation of Nextcloud failed!"
touch "$NEXTCLOUD_DATA_DIR/install.failed"
exit 1
fi
# shellcheck disable=SC2016
installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
INSTALLED_MAJOR="${installed_version%%.*}"
# If a valid upgrade path, trigger the Nextcloud built-in Updater
if ! [ "$INSTALLED_MAJOR" -gt "$IMAGE_MAJOR" ]; then
php /var/www/html/updater/updater.phar --no-interaction --no-backup
if ! php /var/www/html/occ -V || php /var/www/html/occ status | grep maintenance | grep -q 'true'; then
echo "Installation of Nextcloud failed!"
# TODO: Add a hint here about what to do / where to look / updater.log?
touch "$NEXTCLOUD_DATA_DIR/install.failed"
exit 1
fi
# shellcheck disable=SC2016
installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to stay in the parent script

fi
php /var/www/html/occ config:system:set updatechecker --type=bool --value=true
php /var/www/html/occ app:enable nextcloud-aio --force
php /var/www/html/occ db:add-missing-columns
php /var/www/html/occ db:add-missing-primary-keys
yes | php /var/www/html/occ db:convert-filecache-bigint
1 change: 1 addition & 0 deletions php/public/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
$app->post('/api/docker/backup-test', AIO\Controller\DockerController::class . ':StartBackupContainerTest');
$app->post('/api/docker/restore', AIO\Controller\DockerController::class . ':StartBackupContainerRestore');
$app->post('/api/docker/stop', AIO\Controller\DockerController::class . ':StopContainer');
$app->post('/api/docker/nextcloud-upgrade-to-latest-major', AIO\Controller\DockerController::class . ':RunNextcloudUpgradeToLatestMajor');
$app->post('/api/docker/prune', AIO\Controller\DockerController::class . ':SystemPrune');
$app->get('/api/docker/logs', AIO\Controller\DockerController::class . ':GetLogs');
$app->post('/api/auth/login', AIO\Controller\LoginController::class . ':TryLogin');
Expand Down
23 changes: 22 additions & 1 deletion php/src/Controller/DockerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
readonly class DockerController {
private const string TOP_CONTAINER = 'nextcloud-aio-apache';

private function getLatestMajorVersion(): string {
return '33';
}
Comment on lines +18 to +20
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can probably be a constant


public function __construct(
private DockerActionManager $dockerActionManager,
private ContainerDefinitionFetcher $containerDefinitionFetcher,
Expand Down Expand Up @@ -221,7 +225,7 @@ public function StartContainer(Request $request, Response $response, array $args
}

if (isset($request->getParsedBody()['install_latest_major'])) {
$installLatestMajor = '33';
$installLatestMajor = $this->getLatestMajorVersion();
} else {
$installLatestMajor = '';
}
Expand Down Expand Up @@ -328,6 +332,23 @@ public function StopContainer(Request $request, Response $response, array $args)
return $nonbufResp;
}

public function RunNextcloudUpgradeToLatestMajor(Request $request, Response $response, array $args) : Response {
$this->configurationManager->installLatestMajor = $this->getLatestMajorVersion();

// Get streaming response start and closure
$nonbufResp = $this->startStreamingResponse($response);
$body = $nonbufResp->getBody();
$addToStreamingResponseBody = function (string $message) use ($body) : void {
$body->write("<div>" . htmlspecialchars($message, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . "</div>");
};

$this->dockerActionManager->RunNextcloudUpgradeToLatestMajor($addToStreamingResponseBody);

// End streaming response
$this->finalizeStreamingResponse($nonbufResp);
return $nonbufResp;
}

public function SystemPrune(Request $request, Response $response, array $args) : Response {
// Get streaming response start and closure
$nonbufResp = $this->startStreamingResponse($response);
Expand Down
4 changes: 2 additions & 2 deletions php/src/Cron/BackupNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
if (getenv('SEND_SUCCESS_NOTIFICATIONS') === "0") {
error_log("Daily backup successful! Only logging successful backup and not sending backup notification since that has been disabled! You can get further info by looking at the backup logs in the AIO interface.");
} else {
$dockerActionManager->sendNotification($nextcloudContainer, 'Daily backup successful!', 'You can get further info by looking at the backup logs in the AIO interface.');
$dockerActionManager->execCommandInContainer($nextcloudContainer, ['bash', '/notify.sh', 'Daily backup successful!', 'You can get further info by looking at the backup logs in the AIO interface.']);
}
}

if ($backupExitCode > 0) {
$dockerActionManager->sendNotification($nextcloudContainer, 'Daily backup failed!', 'You can get further info by looking at the backup logs in the AIO interface.');
$dockerActionManager->execCommandInContainer($nextcloudContainer, ['bash', '/notify.sh', 'Daily backup failed!', 'You can get further info by looking at the backup logs in the AIO interface.']);
}
2 changes: 1 addition & 1 deletion php/src/Cron/CheckFreeDiskSpace.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
$df = disk_free_space(DataConst::GetDataDirectory());
if ($df !== false && (int)$df < 1024 * 1024 * 1024 * 5) {
error_log("The drive that hosts the mastercontainer volume has less than 5 GB free space. Container updates and backups might not succeed due to that!");
$dockerActionManager->sendNotification($nextcloudContainer, 'Low on space!', 'The drive that hosts the mastercontainer volume has less than 5 GB free space. Container updates and backups might not succeed due to that!');
$dockerActionManager->execCommandInContainer($nextcloudContainer, ['bash', '/notify.sh', 'Low on space!', 'The drive that hosts the mastercontainer volume has less than 5 GB free space. Container updates and backups might not succeed due to that!']);
}
2 changes: 1 addition & 1 deletion php/src/Cron/OutdatedNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
$isNextcloudImageOutdated = $dockerActionManager->isNextcloudImageOutdated();

if ($isNextcloudImageOutdated === true) {
$dockerActionManager->sendNotification($nextcloudContainer, 'AIO is outdated!', 'Please open the AIO interface or ask an administrator to update it. If you do not want to do it manually each time, you can enable the daily backup feature from the AIO interface which automatically updates all containers.', '/notify-all.sh');
$dockerActionManager->execCommandInContainer($nextcloudContainer, ['bash', '/notify-all.sh', 'AIO is outdated!', 'Please open the AIO interface or ask an administrator to update it. If you do not want to do it manually each time, you can enable the daily backup feature from the AIO interface which automatically updates all containers.']);
}

4 changes: 2 additions & 2 deletions php/src/Cron/UpdateNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
$isAnyUpdateAvailable = $dockerActionManager->isAnyUpdateAvailable();

if ($isMastercontainerUpdateAvailable === true) {
$dockerActionManager->sendNotification($nextcloudContainer, 'Mastercontainer update available!', 'Please open your AIO interface to update it. If you do not want to do it manually each time, you can enable the daily backup feature from the AIO interface which also automatically updates the mastercontainer.');
$dockerActionManager->execCommandInContainer($nextcloudContainer, ['bash', '/notify.sh', 'Mastercontainer update available!', 'Please open your AIO interface to update it. If you do not want to do it manually each time, you can enable the daily backup feature from the AIO interface which also automatically updates the mastercontainer.']);
}

if ($isAnyUpdateAvailable === true) {
$dockerActionManager->sendNotification($nextcloudContainer, 'Container updates available!', 'Please open your AIO interface to update them. If you do not want to do it manually each time, you can enable the daily backup feature from the AIO interface which also automatically updates your containers and your Nextcloud apps.');
$dockerActionManager->execCommandInContainer($nextcloudContainer, ['bash', '/notify.sh', 'Container updates available!', 'Please open your AIO interface to update them. If you do not want to do it manually each time, you can enable the daily backup feature from the AIO interface which also automatically updates your containers and your Nextcloud apps.']);
}
96 changes: 64 additions & 32 deletions php/src/Docker/DockerActionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -739,49 +739,76 @@ public function IsMastercontainerUpdateAvailable(): bool {
return true;
}

public function sendNotification(Container $container, string $subject, string $message, string $file = '/notify.sh'): void {
if ($this->GetContainerStartingState($container) === ContainerState::Running) {

$containerName = $container->identifier;
public function execCommandInContainer(Container $container, array $cmd, ?\Closure $outputCallback = null): void {
if ($cmd === []) {
throw new \InvalidArgumentException('$cmd must not be empty.');
}
foreach ($cmd as $arg) {
if (!is_string($arg) || $arg === '') {
throw new \InvalidArgumentException('Every element of $cmd must be a non-empty string.');
}
}

// schedule the exec
$url = $this->BuildApiUrl(sprintf('containers/%s/exec', urlencode($containerName)));
$response = json_decode(
$this->guzzleClient->request(
'POST',
$url,
[
'json' => [
'AttachStdout' => true,
'Tty' => true,
'Cmd' => [
'bash',
$file,
$subject,
$message
],
],
]
)->getBody()->getContents(),
true,
512,
JSON_THROW_ON_ERROR,
);
if ($this->GetContainerStartingState($container) !== ContainerState::Running) {
return;
}

$id = $response['Id'];
$containerName = $container->identifier;

// start the exec
$url = $this->BuildApiUrl(sprintf('exec/%s/start', $id));
// Create exec instance
$url = $this->BuildApiUrl(sprintf('containers/%s/exec', urlencode($containerName)));
$response = json_decode(
$this->guzzleClient->request(
'POST',
$url,
[
'json' => [
'Detach' => false,
'AttachStdout' => true,
'AttachStderr' => true,
'Tty' => true,
'Cmd' => $cmd,
],
]
);
)->getBody()->getContents(),
true,
512,
JSON_THROW_ON_ERROR,
);

$execId = $response['Id'];

// Start exec
$url = $this->BuildApiUrl(sprintf('exec/%s/start', $execId));
$requestOptions = [
'json' => [
'Detach' => false,
'Tty' => true,
],
];
if ($outputCallback !== null) {
$requestOptions['stream'] = true;
}

$startResponse = $this->guzzleClient->request('POST', $url, $requestOptions);

if ($outputCallback !== null) {
$body = $startResponse->getBody();
$buffer = '';
while (!$body->eof()) {
$chunk = $body->read(1024);
$buffer .= $chunk;
while (($pos = strpos($buffer, "\n")) !== false) {
$line = substr($buffer, 0, $pos);
$buffer = substr($buffer, $pos + 1);
$line = rtrim($line, "\r");
if ($line !== '') {
$outputCallback($line);
}
}
}
if (trim($buffer) !== '') {
$outputCallback(trim($buffer));
}
}
}

Expand Down Expand Up @@ -1006,6 +1033,11 @@ public function GetLatestDigestOfTag(string $imageName, string $tag): ?string {
}
}

public function RunNextcloudUpgradeToLatestMajor(\Closure $addToStreamingResponseBody): void {
$container = $this->containerDefinitionFetcher->GetContainerById('nextcloud-aio-nextcloud');
$this->execCommandInContainer($container, ['bash', '/upgrade-latest-major.sh'], $addToStreamingResponseBody);
}

public function SystemPrune(?\Closure $addToStreamingResponseBody = null): void {
$endpoints = [
// Remove stopped containers
Expand Down
7 changes: 6 additions & 1 deletion php/templates/containers.twig
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,12 @@
{% if newMajorVersionString != '' and isAnyRunning == true and isApacheStarting != true %}
<details>
<summary>Note about <strong>Nextcloud Hub {{ newMajorVersionString }}</strong></summary>
<p>If you haven't upgraded to Nextcloud Hub {{ newMajorVersionString }} yet and want to do that now, feel free to follow <strong><a target="_blank" href="https://github.com/nextcloud/all-in-one/discussions/7523">this documentation</a></strong></p>
<p>If you haven't upgraded to Nextcloud Hub {{ newMajorVersionString }} yet and want to do that now, feel free to click the button below. ⚠️ Warning: make sure to create a backup before clicking the button as the update can go wrong and will leave your instance in a broken state!</p>
<form method="POST" action="api/docker/nextcloud-upgrade-to-latest-major" target="overlay-log">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" value="Upgrade to Nextcloud Hub {{ newMajorVersionString }}" data-confirm="Upgrade to Nextcloud Hub {{ newMajorVersionString }}? You should consider creating a backup first." />
</form>
</details>
{% endif %}
{% endif %}
Expand Down
Loading