diff --git a/php/src/Container/Container.php b/php/src/Container/Container.php index 0f9e9de575d..882e1aa28aa 100644 --- a/php/src/Container/Container.php +++ b/php/src/Container/Container.php @@ -5,6 +5,7 @@ use AIO\Data\ConfigurationManager; use AIO\Docker\DockerActionManager; use AIO\ContainerDefinitionFetcher; +use GuzzleHttp\Exception\GuzzleException; readonly class Container { public function __construct( @@ -112,22 +113,15 @@ public function GetVolumes() : ContainerVolumes { return $this->volumes; } + /** @throws GuzzleException */ public function GetRunningState() : ContainerState { return $this->dockerActionManager->GetContainerRunningState($this); } - public function GetRestartingState() : ContainerState { - return $this->dockerActionManager->GetContainerRestartingState($this); - } - - public function GetUpdateState() : VersionState { + public function GetUpdateState() : UpdateState { return $this->dockerActionManager->GetContainerUpdateState($this); } - public function GetStartingState() : ContainerState { - return $this->dockerActionManager->GetContainerStartingState($this); - } - /** * @return string[] */ diff --git a/php/src/Container/ContainerState.php b/php/src/Container/ContainerState.php index f6481027fdd..f679b6e43b9 100644 --- a/php/src/Container/ContainerState.php +++ b/php/src/Container/ContainerState.php @@ -3,10 +3,10 @@ namespace AIO\Container; enum ContainerState: string { - case ImageDoesNotExist = 'image_does_not_exist'; - case NotRestarting = 'not_restarting'; + case DoesNotExist = 'does_not_exist'; case Restarting = 'restarting'; case Running = 'running'; case Starting = 'starting'; case Stopped = 'stopped'; + case Unhealthy = 'unhealthy'; } diff --git a/php/src/Container/UpdateState.php b/php/src/Container/UpdateState.php new file mode 100644 index 00000000000..0fb89587b42 --- /dev/null +++ b/php/src/Container/UpdateState.php @@ -0,0 +1,8 @@ +GetContainerName() . ':' . $tag; } - public function GetContainerRunningState(Container $container) : ContainerState - { + /** @throws GuzzleException */ + public function GetContainerRunningState(Container $container): ContainerState { $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->GetIdentifier()))); try { $response = $this->guzzleClient->get($url); - } catch (RequestException $e) { + } catch (GuzzleException $e) { if ($e->getCode() === 404) { - return ContainerState::ImageDoesNotExist; + return ContainerState::DoesNotExist; } throw $e; } - $responseBody = json_decode((string)$response->getBody(), true); - - if ($responseBody['State']['Running'] === true) { - return ContainerState::Running; + $body = json_decode($response->getBody()->getContents(), true); + assert(is_array($body)); + assert(is_array($body['State'])); + + $state = match ($body['State']['Status']) { + 'running' => ContainerState::Running, + 'created' => ContainerState::Starting, + 'restarting' => ContainerState::Restarting, + 'paused', 'removing', 'exited', 'dead' => ContainerState::Stopped, + default => throw new AssertionError() + }; + + if ($state === ContainerState::Running && is_array($body['State']['Health'])) { + return match ($body['State']['Health']['Status']) { + 'starting' => ContainerState::Starting, + 'unhealthy' => ContainerState::Unhealthy, + default => ContainerState::Running, + }; } else { - return ContainerState::Stopped; + return $state; } } - public function GetContainerRestartingState(Container $container) : ContainerState - { - $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->GetIdentifier()))); - try { - $response = $this->guzzleClient->get($url); - } catch (RequestException $e) { - if ($e->getCode() === 404) { - return ContainerState::ImageDoesNotExist; - } - throw $e; - } - - $responseBody = json_decode((string)$response->getBody(), true); - - if ($responseBody['State']['Restarting'] === true) { - return ContainerState::Restarting; - } else { - return ContainerState::NotRestarting; - } - } - - public function GetContainerUpdateState(Container $container) : VersionState - { + public function GetContainerUpdateState(Container $container): UpdateState { $tag = $container->GetImageTag(); if ($tag === '%AIO_CHANNEL%') { $tag = $this->GetCurrentChannel(); @@ -86,47 +80,19 @@ public function GetContainerUpdateState(Container $container) : VersionState $runningDigests = $this->GetRepoDigestsOfContainer($container->GetIdentifier()); if ($runningDigests === null) { - return VersionState::Different; + return UpdateState::Outdated; } $remoteDigest = $this->dockerHubManager->GetLatestDigestOfTag($container->GetContainerName(), $tag); if ($remoteDigest === null) { - return VersionState::Equal; + return UpdateState::Latest; } - foreach($runningDigests as $runningDigest) { + foreach ($runningDigests as $runningDigest) { if ($runningDigest === $remoteDigest) { - return VersionState::Equal; + return UpdateState::Latest; } } - return VersionState::Different; - } - - public function GetContainerStartingState(Container $container) : ContainerState - { - $runningState = $this->GetContainerRunningState($container); - if ($runningState === ContainerState::Stopped || $runningState === ContainerState::ImageDoesNotExist) { - return $runningState; - } - - $containerName = $container->GetIdentifier(); - $internalPort = $container->GetInternalPort(); - if($internalPort === '%APACHE_PORT%') { - $internalPort = $this->configurationManager->GetApachePort(); - } elseif($internalPort === '%TALK_PORT%') { - $internalPort = $this->configurationManager->GetTalkPort(); - } - - if ($internalPort !== "" && $internalPort !== 'host') { - $connection = @fsockopen($containerName, (int)$internalPort, $errno, $errstr, 0.2); - if ($connection) { - fclose($connection); - return ContainerState::Running; - } else { - return ContainerState::Starting; - } - } else { - return ContainerState::Running; - } + return UpdateState::Outdated; } public function DeleteContainer(Container $container) : void { @@ -614,12 +580,11 @@ public function PullImage(Container $container) : void } } - private function isContainerUpdateAvailable(string $id) : string - { + private function isContainerUpdateAvailable(string $id): string { $container = $this->containerDefinitionFetcher->GetContainerById($id); $updateAvailable = ""; - if ($container->GetUpdateState() === VersionState::Different) { + if ($container->GetUpdateState() === UpdateState::Outdated) { $updateAvailable = '1'; } foreach ($container->GetDependsOn() as $dependency) { @@ -778,9 +743,8 @@ 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) { + public function sendNotification(Container $container, string $subject, string $message, string $file = '/notify.sh'): void { + if ($this->GetContainerRunningState($container) === ContainerState::Running) { $containerName = $container->GetIdentifier(); @@ -961,16 +925,16 @@ public function GetDatabasecontainerExitCode() : int } } - public function isLoginAllowed() : bool { + public function isLoginAllowed(): bool { $id = 'nextcloud-aio-apache'; $apacheContainer = $this->containerDefinitionFetcher->GetContainerById($id); - if ($this->GetContainerStartingState($apacheContainer) === ContainerState::Running) { + if ($this->GetContainerRunningState($apacheContainer) === ContainerState::Running) { return false; } return true; } - public function isBackupContainerRunning() : bool { + public function isBackupContainerRunning(): bool { $id = 'nextcloud-aio-borgbackup'; $backupContainer = $this->containerDefinitionFetcher->GetContainerById($id); if ($this->GetContainerRunningState($backupContainer) === ContainerState::Running) { diff --git a/php/templates/containers.twig b/php/templates/containers.twig index 9bcef1cdc6f..2e37a9eec58 100644 --- a/php/templates/containers.twig +++ b/php/templates/containers.twig @@ -40,19 +40,20 @@ {% endif %} {% for container in containers %} - {% if container.GetDisplayName() != '' and container.GetRunningState().value == 'running' %} + {% set runingState = container.GetRunningState().value %} + {% if container.GetDisplayName() != '' and runingState in ['running', 'unhealthy', 'starting'] %} {% set isAnyRunning = true %} {% endif %} - {% if container.GetDisplayName() != '' and container.GetRestartingState().value == 'restarting' %} + {% if container.GetDisplayName() != '' and runingState == 'restarting' %} {% set isAnyRestarting = true %} {% endif %} - {% if container.GetIdentifier() == 'nextcloud-aio-watchtower' and container.GetRunningState().value == 'running' %} + {% if container.GetIdentifier() == 'nextcloud-aio-watchtower' and runingState in ['running', 'unhealthy', 'starting'] %} {% set isWatchtowerRunning = true %} {% endif %} - {% if container.GetIdentifier() == 'nextcloud-aio-domaincheck' and container.GetRunningState().value == 'running' %} + {% if container.GetIdentifier() == 'nextcloud-aio-domaincheck' and runingState in ['running', 'unhealthy', 'starting'] %} {% set isDomaincheckRunning = true %} {% endif %} - {% if container.GetIdentifier() == 'nextcloud-aio-apache' and container.GetStartingState().value == 'starting' %} + {% if container.GetIdentifier() == 'nextcloud-aio-apache' and runingState == 'starting' %} {% set isApacheStarting = true %} {% endif %} {% endfor %} @@ -261,14 +262,29 @@ {% for container in containers %} {% if container.GetDisplayName() != '' %}
  • - {% if container.GetStartingState().value == 'starting' %} + {% set runningState = container.GetRunningState().value %} + {% if runningState == 'starting' %} {{ container.GetDisplayName() }} (Starting) {% if container.GetDocumentation() != '' %} (docs) {% endif %} - {% elseif container.GetRunningState().value == 'running' %} + {% elseif runningState == 'unhealthy' %} + + {{ container.GetDisplayName() }} (Unhealthy) + {% if container.GetDocumentation() != '' %} + (docs) + {% endif %} + + {% elseif runningState == 'restarting' %} + + {{ container.GetDisplayName() }} (Restarting) + {% if container.GetDocumentation() != '' %} + (docs) + {% endif %} + + {% elseif runningState == 'running' %} {{ container.GetDisplayName() }} (Running) {% if container.GetDocumentation() != '' %}