From a58ff460faeaacd18124be1168618d0b5b926d41 Mon Sep 17 00:00:00 2001 From: Sergei Shitikov Date: Sun, 29 Sep 2024 14:46:54 +0200 Subject: [PATCH] refactored WaitForHealthCheck strategy and added more granular exceptions --- src/Exception/ContainerException.php | 21 ++++++ src/Exception/ContainerNotReadyException.php | 6 +- src/Exception/ContainerStateException.php | 14 ++++ .../ContainerWaitingTimeoutException.php | 14 +--- src/Exception/HealthCheckFailedException.php | 14 ++++ .../HealthCheckNotConfiguredException.php | 14 ++++ .../UnknownHealthStatusException.php | 14 ++++ src/Wait/WaitForHealthCheck.php | 73 +++++++++++++------ 8 files changed, 131 insertions(+), 39 deletions(-) create mode 100644 src/Exception/ContainerException.php create mode 100644 src/Exception/ContainerStateException.php create mode 100644 src/Exception/HealthCheckFailedException.php create mode 100644 src/Exception/HealthCheckNotConfiguredException.php create mode 100644 src/Exception/UnknownHealthStatusException.php diff --git a/src/Exception/ContainerException.php b/src/Exception/ContainerException.php new file mode 100644 index 0000000..f0d02a3 --- /dev/null +++ b/src/Exception/ContainerException.php @@ -0,0 +1,21 @@ +containerId = $containerId; + parent::__construct($message, 0, $previous); + } + + public function getContainerId(): string + { + return $this->containerId; + } +} diff --git a/src/Exception/ContainerNotReadyException.php b/src/Exception/ContainerNotReadyException.php index 1b5f677..200983f 100644 --- a/src/Exception/ContainerNotReadyException.php +++ b/src/Exception/ContainerNotReadyException.php @@ -4,10 +4,6 @@ declare(strict_types=1); namespace Testcontainers\Exception; -class ContainerNotReadyException extends \RuntimeException +class ContainerNotReadyException extends ContainerException { - public function __construct(string $id, ?\Throwable $previous = null) - { - parent::__construct(sprintf('Container %s is not ready', $id), 0, $previous); - } } diff --git a/src/Exception/ContainerStateException.php b/src/Exception/ContainerStateException.php new file mode 100644 index 0000000..c91cb07 --- /dev/null +++ b/src/Exception/ContainerStateException.php @@ -0,0 +1,14 @@ +containerId = $containerId; $message ??= sprintf('Timeout reached while waiting for container %s', $containerId); - parent::__construct($message, 0, $previous); + parent::__construct($message, $containerId, $previous); } - - public function getContainerId(): string - { - return $this->containerId; - } -} +} \ No newline at end of file diff --git a/src/Exception/HealthCheckFailedException.php b/src/Exception/HealthCheckFailedException.php new file mode 100644 index 0000000..b1b7c08 --- /dev/null +++ b/src/Exception/HealthCheckFailedException.php @@ -0,0 +1,14 @@ + $this->timeout) { - throw new TimeoutException(sprintf("Health check not healthy after %d ms", $this->timeout)); + throw new ContainerWaitingTimeoutException($container->getId()); } - /** @var \Psr\Http\Message\ResponseInterface | null $containerInspect */ - $containerInspect = $container->getClient()->containerInspect($container->getId(), [], Docker::FETCH_RESPONSE); - //$containerStatus = $containerInspect?->getArrayCopy() ?? null; - $containerStatus = ''; - if ($containerStatus === 'healthy') { - return; + /** @var ContainersIdJsonGetResponse200|null $containerInspect */ + $containerInspect = $container->getClient()->containerInspect($container->getId()); + + $containerState = $containerInspect?->getState(); + + if ($containerState !== null) { + $health = $containerState->getHealth(); + + if ($health !== null) { + $status = $health->getStatus(); + + switch ($status) { + case 'healthy': + return; // Container is healthy + case 'starting': + // Health check is still in progress; continue waiting + break; + case 'unhealthy': + throw new HealthCheckFailedException($container->getId()); + case 'none': + throw new HealthCheckNotConfiguredException($container->getId()); + default: + throw new UnknownHealthStatusException($container->getId(), (string)$status); + } + } else { + // Health is null; treat as 'none' status + throw new HealthCheckNotConfiguredException($container->getId()); + } + } else { + // Container state is null + throw new ContainerStateException($container->getId()); } - if ($containerStatus === 'unhealthy') { - throw new ContainerNotReadyException(sprintf("Health check failed: %s", $containerStatus)); - } - - usleep($this->pollInterval * 1000); // Sleep for the polling interval + usleep($this->pollInterval * 1000); } } }