all-in-one/php/src/Controller/DockerController.php

300 lines
11 KiB
PHP
Raw Normal View History

2021-11-30 11:20:42 +01:00
<?php
namespace AIO\Controller;
use AIO\Container\ContainerState;
2021-11-30 11:20:42 +01:00
use AIO\ContainerDefinitionFetcher;
use AIO\Docker\DockerActionManager;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use AIO\Data\ConfigurationManager;
readonly class DockerController {
private const string TOP_CONTAINER = 'nextcloud-aio-apache';
2021-11-30 11:20:42 +01:00
public function __construct(
private DockerActionManager $dockerActionManager,
private ContainerDefinitionFetcher $containerDefinitionFetcher,
private ConfigurationManager $configurationManager
2021-11-30 11:20:42 +01:00
) {
}
private function PerformRecursiveContainerStart(string $id, bool $pullImage = true) : void {
2021-11-30 11:20:42 +01:00
$container = $this->containerDefinitionFetcher->GetContainerById($id);
// Start all dependencies first and then itself
2021-11-30 11:20:42 +01:00
foreach($container->GetDependsOn() as $dependency) {
$this->PerformRecursiveContainerStart($dependency, $pullImage);
2021-11-30 11:20:42 +01:00
}
// Don't start if container is already running
// This is expected to happen if a container is defined in depends_on of multiple containers
if ($container->GetRunningState() === ContainerState::Running) {
error_log('Not starting ' . $id . ' because it was already started.');
return;
}
2021-11-30 11:20:42 +01:00
$this->dockerActionManager->DeleteContainer($container);
$this->dockerActionManager->CreateVolumes($container);
$this->dockerActionManager->PullImage($container, $pullImage);
2021-11-30 11:20:42 +01:00
$this->dockerActionManager->CreateContainer($container);
$this->dockerActionManager->StartContainer($container);
$this->dockerActionManager->ConnectContainerToNetwork($container);
}
private function PerformRecursiveImagePull(string $id) : void {
$container = $this->containerDefinitionFetcher->GetContainerById($id);
// Pull all dependencies first and then itself
foreach($container->GetDependsOn() as $dependency) {
$this->PerformRecursiveImagePull($dependency);
}
$this->dockerActionManager->PullImage($container, true);
}
public function PullAllContainerImages(): void {
$id = self::TOP_CONTAINER;
$this->PerformRecursiveImagePull($id);
}
public function GetLogs(Request $request, Response $response, array $args) : Response
2021-11-30 11:20:42 +01:00
{
$requestParams = $request->getQueryParams();
$id = '';
if (isset($requestParams['id']) && is_string($requestParams['id'])) {
$id = $requestParams['id'];
}
if (str_starts_with($id, 'nextcloud-aio-')) {
$logs = $this->dockerActionManager->GetLogs($id);
} else {
$logs = 'Container not found.';
}
2021-11-30 11:20:42 +01:00
$body = $response->getBody();
$body->write($logs);
return $response
->withStatus(200)
->withHeader('Content-Type', 'text/plain; charset=utf-8')
->withHeader('Content-Disposition', 'inline');
}
public function StartBackupContainerBackup(Request $request, Response $response, array $args) : Response {
$this->startBackup();
return $response->withStatus(201)->withHeader('Location', '/');
}
public function startBackup() : void {
2021-11-30 11:20:42 +01:00
$config = $this->configurationManager->GetConfig();
$config['backup-mode'] = 'backup';
$this->configurationManager->WriteConfig($config);
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStop($id);
$id = 'nextcloud-aio-borgbackup';
$this->PerformRecursiveContainerStart($id);
}
public function StartBackupContainerCheck(Request $request, Response $response, array $args) : Response {
$this->checkBackup();
return $response->withStatus(201)->withHeader('Location', '/');
}
public function checkBackup() : void {
2021-11-30 11:20:42 +01:00
$config = $this->configurationManager->GetConfig();
$config['backup-mode'] = 'check';
$this->configurationManager->WriteConfig($config);
$id = 'nextcloud-aio-borgbackup';
$this->PerformRecursiveContainerStart($id);
}
public function StartBackupContainerRestore(Request $request, Response $response, array $args) : Response {
2021-11-30 11:20:42 +01:00
$config = $this->configurationManager->GetConfig();
$config['backup-mode'] = 'restore';
$config['selected-restore-time'] = $request->getParsedBody()['selected_restore_time'] ?? '';
if (isset($request->getParsedBody()['restore-exclude-previews'])) {
$config['restore-exclude-previews'] = 1;
} else {
$config['restore-exclude-previews'] = '';
}
2021-11-30 11:20:42 +01:00
$this->configurationManager->WriteConfig($config);
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStop($id);
$id = 'nextcloud-aio-borgbackup';
$this->PerformRecursiveContainerStart($id);
return $response->withStatus(201)->withHeader('Location', '/');
}
public function StartBackupContainerCheckRepair(Request $request, Response $response, array $args) : Response {
$config = $this->configurationManager->GetConfig();
$config['backup-mode'] = 'check-repair';
$this->configurationManager->WriteConfig($config);
$id = 'nextcloud-aio-borgbackup';
$this->PerformRecursiveContainerStart($id);
// Restore to backup check which is needed to make the UI logic work correctly
$config = $this->configurationManager->GetConfig();
$config['backup-mode'] = 'check';
$this->configurationManager->WriteConfig($config);
return $response->withStatus(201)->withHeader('Location', '/');
}
public function StartBackupContainerTest(Request $request, Response $response, array $args) : Response {
$config = $this->configurationManager->GetConfig();
$config['backup-mode'] = 'test';
$config['instance_restore_attempt'] = 0;
$this->configurationManager->WriteConfig($config);
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStop($id);
$id = 'nextcloud-aio-borgbackup';
$this->PerformRecursiveContainerStart($id);
return $response->withStatus(201)->withHeader('Location', '/');
}
public function StartContainer(Request $request, Response $response, array $args) : Response
2021-11-30 11:20:42 +01:00
{
$uri = $request->getUri();
$host = $uri->getHost();
$port = $uri->getPort();
if ($port === 8000) {
error_log('The AIO_URL-port was discovered to be 8000 which is not expected. It is now set to 443.');
$port = 443;
}
2021-11-30 11:20:42 +01:00
if (isset($request->getParsedBody()['install_latest_major'])) {
$installLatestMajor = 31;
} else {
$installLatestMajor = "";
}
2021-11-30 11:20:42 +01:00
$config = $this->configurationManager->GetConfig();
// set AIO_URL
$config['AIO_URL'] = $host . ':' . $port;
// set wasStartButtonClicked
$config['wasStartButtonClicked'] = 1;
// set install_latest_major
$config['install_latest_major'] = $installLatestMajor;
$this->configurationManager->WriteConfig($config);
// Start container
$this->startTopContainer(true);
// Clear apcu cache in order to check if container updates are available
// Temporarily disabled as it leads much faster to docker rate limits
// apcu_clear_cache();
return $response->withStatus(201)->withHeader('Location', '/');
}
public function startTopContainer(bool $pullImage) : void {
$config = $this->configurationManager->GetConfig();
2021-11-30 11:20:42 +01:00
// set AIO_TOKEN
$config['AIO_TOKEN'] = bin2hex(random_bytes(24));
$this->configurationManager->WriteConfig($config);
// Stop domaincheck since apache would not be able to start otherwise
$this->StopDomaincheckContainer();
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStart($id, $pullImage);
2021-11-30 11:20:42 +01:00
}
public function StartWatchtowerContainer(Request $request, Response $response, array $args) : Response {
$this->startWatchtower();
return $response->withStatus(201)->withHeader('Location', '/');
}
public function startWatchtower() : void {
2021-11-30 11:20:42 +01:00
$id = 'nextcloud-aio-watchtower';
$this->PerformRecursiveContainerStart($id);
}
private function PerformRecursiveContainerStop(string $id) : void
2021-11-30 11:20:42 +01:00
{
$container = $this->containerDefinitionFetcher->GetContainerById($id);
// This is a hack but no better solution was found for the meantime
// Stop Collabora first to make sure it force-saves
// See https://github.com/nextcloud/richdocuments/issues/3799
if ($id === self::TOP_CONTAINER) {
$this->PerformRecursiveContainerStop('nextcloud-aio-collabora');
2021-11-30 11:20:42 +01:00
}
// Stop itself first and then all the dependencies
2021-11-30 11:20:42 +01:00
$this->dockerActionManager->StopContainer($container);
foreach($container->GetDependsOn() as $dependency) {
$this->PerformRecursiveContainerStop($dependency);
}
2021-11-30 11:20:42 +01:00
}
public function StopContainer(Request $request, Response $response, array $args) : Response
2021-11-30 11:20:42 +01:00
{
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStop($id);
return $response->withStatus(201)->withHeader('Location', '/');
}
public function stopTopContainer() : void {
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStop($id);
}
public function StartDomaincheckContainer() : void
2021-11-30 11:20:42 +01:00
{
# Don't start if domain is already set
if ($this->configurationManager->GetDomain() !== '' || $this->configurationManager->wasStartButtonClicked()) {
2021-11-30 11:20:42 +01:00
return;
}
$id = 'nextcloud-aio-domaincheck';
$cacheKey = 'domaincheckWasStarted';
$domaincheckContainer = $this->containerDefinitionFetcher->GetContainerById($id);
$apacheContainer = $this->containerDefinitionFetcher->GetContainerById(self::TOP_CONTAINER);
// Don't start if apache is already running
if ($apacheContainer->GetRunningState() === ContainerState::Running) {
2021-11-30 11:20:42 +01:00
return;
// Don't start if domaincheck is already running
} elseif ($domaincheckContainer->GetRunningState() === ContainerState::Running) {
$domaincheckWasStarted = apcu_fetch($cacheKey);
// Start domaincheck again when 10 minutes are over by not returning here
if($domaincheckWasStarted !== false && is_string($domaincheckWasStarted)) {
return;
}
2021-11-30 11:20:42 +01:00
}
$this->StopDomaincheckContainer();
try {
$this->PerformRecursiveContainerStart($id);
} catch (\Exception $e) {
error_log('Could not start domaincheck container: ' . $e->getMessage());
}
// Cache the start for 10 minutes
apcu_add($cacheKey, '1', 600);
2021-11-30 11:20:42 +01:00
}
private function StopDomaincheckContainer() : void
2021-11-30 11:20:42 +01:00
{
$id = 'nextcloud-aio-domaincheck';
$this->PerformRecursiveContainerStop($id);
}
}