mirror of
https://github.com/stan220/testcontainers-php.git
synced 2026-09-08 17:29:29 +00:00
first working version with simple WaitForContainerRunning implementation
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Testcontainers\Container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Added for backward compatibility.
|
||||||
|
* Just a wrapper for GenericContainer.
|
||||||
|
* @deprecated Use GenericContainer instead.
|
||||||
|
*/
|
||||||
|
class Container extends GenericContainer
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -4,17 +4,18 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Testcontainers\Container;
|
namespace Testcontainers\Container;
|
||||||
|
|
||||||
|
use Docker\API\Exception\ContainerCreateNotFoundException;
|
||||||
use Docker\API\Model\ContainersCreatePostBody;
|
use Docker\API\Model\ContainersCreatePostBody;
|
||||||
use Docker\API\Model\ContainersIdExecPostBody;
|
use Docker\API\Model\ContainersIdExecPostBody;
|
||||||
use Docker\API\Model\EndpointSettings;
|
|
||||||
use Docker\API\Model\HealthConfig;
|
use Docker\API\Model\HealthConfig;
|
||||||
|
use Docker\API\Model\HostConfig;
|
||||||
use Docker\API\Model\Mount;
|
use Docker\API\Model\Mount;
|
||||||
use Docker\API\Model\NetworkingConfig;
|
use Docker\API\Model\PortBinding;
|
||||||
use Docker\API\Model\Port;
|
|
||||||
use Docker\Docker;
|
use Docker\Docker;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Testcontainers\Exception\ContainerNotReadyException;
|
use Testcontainers\ContainerRuntime\ContainerRuntimeClient;
|
||||||
use Testcontainers\Registry;
|
use Testcontainers\Registry;
|
||||||
|
use Testcontainers\Wait\WaitForContainerRunning;
|
||||||
use Testcontainers\Wait\WaitInterface;
|
use Testcontainers\Wait\WaitInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,7 +47,7 @@ class GenericContainer
|
|||||||
|
|
||||||
protected WaitInterface $wait;
|
protected WaitInterface $wait;
|
||||||
|
|
||||||
protected bool $privileged = false;
|
protected bool $isPrivileged = false;
|
||||||
protected ?string $networkName = null;
|
protected ?string $networkName = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,17 +55,19 @@ class GenericContainer
|
|||||||
*/
|
*/
|
||||||
protected array $mounts = [];
|
protected array $mounts = [];
|
||||||
|
|
||||||
/**
|
/** @var array<string> List of exposed ports in the format ['8080/tcp'] */
|
||||||
* @var array<Port>
|
protected array $exposedPorts = [];
|
||||||
*/
|
|
||||||
protected array $ports = [];
|
|
||||||
|
|
||||||
protected function __construct(string $image)
|
public function __construct(string $image)
|
||||||
{
|
{
|
||||||
$this->image = $image;
|
$this->image = $image;
|
||||||
$this->dockerClient = Docker::create();
|
$this->dockerClient = ContainerRuntimeClient::getDockerClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use constructor instead
|
||||||
|
* Left for backward compatibility
|
||||||
|
*/
|
||||||
public static function make(string $image): self
|
public static function make(string $image): self
|
||||||
{
|
{
|
||||||
return new GenericContainer($image);
|
return new GenericContainer($image);
|
||||||
@@ -120,16 +123,67 @@ class GenericContainer
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use `withExposedPorts` instead
|
||||||
|
*/
|
||||||
public function withPort(string $localPort, string $containerPort): self
|
public function withPort(string $localPort, string $containerPort): self
|
||||||
{
|
{
|
||||||
$this->ports[] = new Port(['privatePort' => (int) $containerPort, 'publicPort' => (int) $localPort]);
|
return $this->withExposedPorts($containerPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-param string|int|array<string|int> $port
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Add ports to be exposed by the Docker container.
|
||||||
|
* This method accepts multiple inputs: single port, multiple ports, or ports with specific protocols
|
||||||
|
* to attempt to align with other language implementations.
|
||||||
|
*
|
||||||
|
* @psalm-param int|string|array<int|string> $ports One or more ports to expose.
|
||||||
|
* @return self Fluent interface for chaining.
|
||||||
|
*/
|
||||||
|
public function withExposedPorts(...$ports): self
|
||||||
|
{
|
||||||
|
foreach ($ports as $port) {
|
||||||
|
if (is_array($port)) {
|
||||||
|
// Flatten the array and recurse
|
||||||
|
$this->withExposedPorts(...$port);
|
||||||
|
} else {
|
||||||
|
// Handle single port entry, either string or int
|
||||||
|
$this->exposedPorts[] = $this->normalizePort($port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port specification to ensure it includes a protocol.
|
||||||
|
* Defaults to 'tcp' if no protocol is specified.
|
||||||
|
*
|
||||||
|
* @param string|int $port Port to normalize.
|
||||||
|
* @return string Normalized port string.
|
||||||
|
*
|
||||||
|
* TODO: move this to a utility class
|
||||||
|
*/
|
||||||
|
private function normalizePort(string|int $port): string
|
||||||
|
{
|
||||||
|
if (is_int($port)) {
|
||||||
|
// Direct integer ports default to tcp
|
||||||
|
return "{$port}/tcp";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the port specification already includes a protocol
|
||||||
|
if (is_string($port) && !str_contains($port, '/')) {
|
||||||
|
return "{$port}/tcp";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $port;
|
||||||
|
}
|
||||||
|
|
||||||
public function withPrivileged(bool $privileged = true): self
|
public function withPrivileged(bool $privileged = true): self
|
||||||
{
|
{
|
||||||
$this->privileged = $privileged;
|
$this->isPrivileged = $privileged;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -141,72 +195,12 @@ class GenericContainer
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(bool $wait = true): self
|
public function wait(): self
|
||||||
{
|
{
|
||||||
$this->containerName = uniqid('testcontainer', true);
|
$this->wait->wait($this->id);
|
||||||
|
|
||||||
$this->containerConfig = new ContainersCreatePostBody();
|
|
||||||
$this->containerConfig->setImage($this->image);
|
|
||||||
|
|
||||||
$envs = [];
|
|
||||||
foreach ($this->env as $name => $value) {
|
|
||||||
$envs[] = $name . '=' . $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->containerConfig->setEnv($envs);
|
|
||||||
|
|
||||||
if ($this->healthConfig !== null) {
|
|
||||||
$this->containerConfig->setHealthcheck($this->healthConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->networkName !== null) {
|
|
||||||
$this->containerConfig->setNetworkingConfig(new NetworkingConfig([
|
|
||||||
'endpointsConfig' => [
|
|
||||||
$this->networkName => new EndpointSettings([
|
|
||||||
'aliases' => [$this->containerName],
|
|
||||||
'networkID' => $this->networkName,
|
|
||||||
]),
|
|
||||||
]]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->entryPoint !== null) {
|
|
||||||
$this->containerConfig->setEntrypoint([$this->entryPoint]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->privileged) {
|
|
||||||
//TODO: Implement privileged mode
|
|
||||||
}
|
|
||||||
|
|
||||||
$containerCreateResponse = $this->dockerClient->containerCreate($this->containerConfig, ['name' => $this->containerName]);
|
|
||||||
|
|
||||||
$this->id = $containerCreateResponse->getId();
|
|
||||||
|
|
||||||
Registry::add($this);
|
|
||||||
|
|
||||||
if ($wait) {
|
|
||||||
$this->wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function wait(int $wait = 100): self
|
|
||||||
{
|
|
||||||
usleep(500000);
|
|
||||||
return $this;
|
|
||||||
|
|
||||||
// for ($i = 0; $i < $wait; $i++) {
|
|
||||||
// try {
|
|
||||||
// $this->dockerClient->containerWait($this->id);
|
|
||||||
// return $this;
|
|
||||||
// } catch (ContainerNotReadyException $e) {
|
|
||||||
// usleep(500000);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// throw new ContainerNotReadyException($this->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function stop(): self
|
public function stop(): self
|
||||||
{
|
{
|
||||||
$this->dockerClient->containerStop($this->id);
|
$this->dockerClient->containerStop($this->id);
|
||||||
@@ -216,8 +210,41 @@ class GenericContainer
|
|||||||
|
|
||||||
public function start(): self
|
public function start(): self
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
|
$containerCreatePostBody = new ContainersCreatePostBody();
|
||||||
|
$portMap = new \ArrayObject();
|
||||||
|
|
||||||
|
foreach ($this->exposedPorts as $port) {
|
||||||
|
$portBinding = new PortBinding();
|
||||||
|
$portBinding->setHostPort(explode('/', $port)[0]);
|
||||||
|
$portBinding->setHostIp('0.0.0.0');
|
||||||
|
$portMap[$port] = [$portBinding];
|
||||||
|
}
|
||||||
|
|
||||||
|
$hostConfig = new HostConfig();
|
||||||
|
$hostConfig->setPortBindings($portMap);
|
||||||
|
$containerCreatePostBody->setHostConfig($hostConfig);
|
||||||
|
$containerCreatePostBody->setImage($this->image);
|
||||||
|
//$containerCreatePostBody->setEnv($this->env);
|
||||||
|
|
||||||
|
$containerCreateResponse = $this->dockerClient->containerCreate($containerCreatePostBody);
|
||||||
|
$this->id = $containerCreateResponse?->getId() ?? '';
|
||||||
|
} catch (ContainerCreateNotFoundException) {
|
||||||
|
$this->dockerClient->imageCreate(null, [
|
||||||
|
'fromImage' => explode(':', $this->image)[0],
|
||||||
|
'tag' => explode(':', $this->image)[1] ?? 'latest',
|
||||||
|
]);
|
||||||
|
return $this->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
Registry::add($this);
|
||||||
|
|
||||||
$this->dockerClient->containerStart($this->id);
|
$this->dockerClient->containerStart($this->id);
|
||||||
|
|
||||||
|
if(!isset($this->wait)) {
|
||||||
|
$this->withWait(new WaitForContainerRunning());
|
||||||
|
}
|
||||||
|
$this->wait();
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,6 +257,7 @@ class GenericContainer
|
|||||||
|
|
||||||
public function remove(): self
|
public function remove(): self
|
||||||
{
|
{
|
||||||
|
$this->dockerClient->containerStop($this->id);
|
||||||
$this->dockerClient->containerDelete($this->id);
|
$this->dockerClient->containerDelete($this->id);
|
||||||
|
|
||||||
Registry::remove($this);
|
Registry::remove($this);
|
||||||
@@ -244,6 +272,15 @@ class GenericContainer
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use `start` instead
|
||||||
|
* Left for backward compatibility
|
||||||
|
*/
|
||||||
|
public function run(): self
|
||||||
|
{
|
||||||
|
return $this->start();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string> $commandAsArray
|
* @param array<string> $commandAsArray
|
||||||
*/
|
*/
|
||||||
@@ -261,15 +298,39 @@ class GenericContainer
|
|||||||
|
|
||||||
public function getAddress(): string
|
public function getAddress(): string
|
||||||
{
|
{
|
||||||
$containerNetworks = $this->dockerClient->containerInspect($this->id)
|
$inspection = $this->inspect();
|
||||||
->getNetworkSettings()->getNetworks();
|
return $inspection['gateway'];
|
||||||
$containerAddress = '';
|
// foreach ($containerNetworks as $network) {
|
||||||
foreach ($containerNetworks as $network) {
|
// var_dump($network->getNetworkID(), $this->id, $network->getIPAddress());
|
||||||
if($network->getNetworkID() === $this->id) {
|
// if($network->getNetworkID() === $this->id) {
|
||||||
$containerAddress = $network->getIpAddress();
|
// $containerAddress = $network->getIpAddress();
|
||||||
break;
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return $containerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{gateway: string, ports: array<string, int>}
|
||||||
|
*/
|
||||||
|
public function inspect(): array
|
||||||
|
{
|
||||||
|
$response = $this->dockerClient->containerInspect($this->id);
|
||||||
|
$settings = $response->getNetworkSettings();
|
||||||
|
var_dump($settings);
|
||||||
|
|
||||||
|
$ports = [];
|
||||||
|
foreach ($settings->getPorts() as $port => $value) {
|
||||||
|
if ($value === null) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ports[$port] = (int) $value[0]->getHostPort();
|
||||||
}
|
}
|
||||||
return $containerAddress;
|
|
||||||
|
return [
|
||||||
|
'gateway' => $settings->getGateway(),
|
||||||
|
'ports' => $ports,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -32,8 +32,8 @@ class Registry
|
|||||||
|
|
||||||
public static function cleanup(): void
|
public static function cleanup(): void
|
||||||
{
|
{
|
||||||
foreach (self::$registry as $container) {
|
// foreach (self::$registry as $container) {
|
||||||
$container->remove();
|
// $container->remove();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Testcontainers\Trait;
|
|
||||||
|
|
||||||
use JsonException;
|
|
||||||
use Symfony\Component\Process\Process;
|
|
||||||
use Testcontainers\Container\GenericContainer;
|
|
||||||
use UnexpectedValueException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @phpstan-import-type ContainerInspect from GenericContainer
|
|
||||||
* @phpstan-import-type DockerNetwork from GenericContainer
|
|
||||||
*/
|
|
||||||
trait DockerContainerAwareTrait
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param string $containerId
|
|
||||||
* @param string|null $networkName
|
|
||||||
* @param ContainerInspect|null $inspectedData
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws JsonException
|
|
||||||
*/
|
|
||||||
private static function dockerContainerAddress(string $containerId, ?string $networkName = null, ?array $inspectedData = null): string
|
|
||||||
{
|
|
||||||
if (! is_array($inspectedData)) {
|
|
||||||
$inspectedData = self::dockerContainerInspect($containerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_string($networkName)) {
|
|
||||||
$containerAddress = $inspectedData[0]['NetworkSettings']['Networks'][$networkName]['IPAddress'] ?? null;
|
|
||||||
|
|
||||||
if (is_string($containerAddress)) {
|
|
||||||
return $containerAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$containerAddress = $inspectedData[0]['NetworkSettings']['IPAddress'] ?? null;
|
|
||||||
|
|
||||||
if (is_string($containerAddress)) {
|
|
||||||
return $containerAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new UnexpectedValueException('Unable to find container IP address');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $containerId
|
|
||||||
* @return ContainerInspect
|
|
||||||
*
|
|
||||||
* @throws JsonException
|
|
||||||
*/
|
|
||||||
private static function dockerContainerInspect(string $containerId): array
|
|
||||||
{
|
|
||||||
$process = new Process(['docker', 'inspect', $containerId]);
|
|
||||||
$process->mustRun();
|
|
||||||
|
|
||||||
/** @var ContainerInspect */
|
|
||||||
return json_decode($process->getOutput(), true, 512, JSON_THROW_ON_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $networkName
|
|
||||||
* @return DockerNetwork|false
|
|
||||||
*
|
|
||||||
* @throws JsonException
|
|
||||||
*/
|
|
||||||
private static function dockerNetworkFind(string $networkName): array|false
|
|
||||||
{
|
|
||||||
$process = new Process(['docker', 'network', 'ls', '--format', 'json', '--filter', 'name=' . $networkName]);
|
|
||||||
$process->mustRun();
|
|
||||||
|
|
||||||
$json = $process->getOutput();
|
|
||||||
|
|
||||||
if ($json === '') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$json = str_replace("\n", ',', $json);
|
|
||||||
$json = '['. rtrim($json, ',') .']';
|
|
||||||
|
|
||||||
/** @var array<int, DockerNetwork> $output */
|
|
||||||
$output = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
|
|
||||||
|
|
||||||
/** @var array<int, DockerNetwork> $matchingNetworks */
|
|
||||||
$matchingNetworks = array_filter($output, static fn (array $network) => $network['Name'] === $networkName);
|
|
||||||
|
|
||||||
if (count($matchingNetworks) === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $matchingNetworks[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function dockerNetworkCreate(string $networkName, string $driver = 'bridge'): void
|
|
||||||
{
|
|
||||||
$process = new Process(['docker', 'network', 'create', '--driver', $driver, $networkName]);
|
|
||||||
$process->mustRun();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function dockerNetworkRemove(string $networkName): void
|
|
||||||
{
|
|
||||||
$process = new Process(['docker', 'network', 'rm', $networkName, '-f']);
|
|
||||||
$process->mustRun();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Testcontainers\Wait;
|
||||||
|
|
||||||
|
use Docker\API\Model\ContainersIdJsonGetResponse200;
|
||||||
|
use Docker\Docker;
|
||||||
|
use Testcontainers\ContainerRuntime\ContainerRuntimeClient;
|
||||||
|
use Testcontainers\Exception\ContainerNotReadyException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simply makes container inspect and checks if container is running.
|
||||||
|
* Uses $timout and $pollInterval in milliseconds to set the parameters for waiting.
|
||||||
|
*/
|
||||||
|
class WaitForContainerRunning implements WaitInterface
|
||||||
|
{
|
||||||
|
protected Docker $dockerClient;
|
||||||
|
|
||||||
|
public function __construct(protected int $timeout = 10000, protected int $pollInterval = 500)
|
||||||
|
{
|
||||||
|
$this->dockerClient = ContainerRuntimeClient::getDockerClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function wait(string $id): void
|
||||||
|
{
|
||||||
|
$startTime = microtime(true) * 1000;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$elapsedTime = (microtime(true) * 1000) - $startTime;
|
||||||
|
|
||||||
|
if ($elapsedTime > $this->timeout) {
|
||||||
|
throw new ContainerNotReadyException($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var ContainersIdJsonGetResponse200 | null $containerInspect */
|
||||||
|
$containerInspect = $this->dockerClient->containerInspect($id);
|
||||||
|
$containerStatus = $containerInspect?->getState()?->getStatus();
|
||||||
|
|
||||||
|
if ($containerStatus === 'running') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep($this->pollInterval * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,24 +5,48 @@ declare(strict_types=1);
|
|||||||
namespace Testcontainers\Wait;
|
namespace Testcontainers\Wait;
|
||||||
|
|
||||||
use Docker\Docker;
|
use Docker\Docker;
|
||||||
|
use Docker\DockerClientFactory;
|
||||||
|
use Http\Client\Socket\Exception\TimeoutException;
|
||||||
|
use Testcontainers\ContainerRuntime\ContainerRuntimeClient;
|
||||||
use Testcontainers\Exception\ContainerNotReadyException;
|
use Testcontainers\Exception\ContainerNotReadyException;
|
||||||
|
|
||||||
class WaitForHealthCheck implements WaitInterface
|
class WaitForHealthCheck implements WaitInterface
|
||||||
{
|
{
|
||||||
protected Docker $dockerClient;
|
protected Docker $dockerClient;
|
||||||
|
protected int $timeout;
|
||||||
|
protected int $pollInterval;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct(int $timeout = 5000, int $pollInterval = 1000)
|
||||||
{
|
{
|
||||||
$this->dockerClient = Docker::create();
|
$this->dockerClient = ContainerRuntimeClient::getDockerClient();
|
||||||
|
$this->timeout = $timeout;
|
||||||
|
$this->pollInterval = $pollInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function wait(string $id): void
|
public function wait(string $id): void
|
||||||
{
|
{
|
||||||
$containerInspect = $this->dockerClient->containerInspect($id);
|
$startTime = microtime(true) * 1000;
|
||||||
$containerInspect->getBody()->getContents();
|
|
||||||
dd($containerInspect->getStatusCode());
|
|
||||||
|
|
||||||
if ($status !== 'healthy') {
|
while (true) {
|
||||||
throw new ContainerNotReadyException($id);
|
$elapsedTime = (microtime(true) * 1000) - $startTime;
|
||||||
|
|
||||||
|
if ($elapsedTime > $this->timeout) {
|
||||||
|
throw new TimeoutException(sprintf("Health check not healthy after %d ms", $this->timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
$containerInspect = $this->dockerClient->containerInspect($id, [], Docker::FETCH_RESPONSE);
|
||||||
|
//$containerStatus = $containerInspect?->getArrayCopy() ?? null;
|
||||||
|
var_dump($containerInspect->getBody()->getContents());
|
||||||
|
$containerStatus='';
|
||||||
|
if ($containerStatus === 'healthy') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($containerStatus === 'unhealthy') {
|
||||||
|
throw new ContainerNotReadyException(sprintf("Health check failed: %s", $containerStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep($this->pollInterval * 1000); // Sleep for the polling interval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class ContainerTest extends TestCase
|
|||||||
$container->withMySQLUser('bar', 'baz');
|
$container->withMySQLUser('bar', 'baz');
|
||||||
|
|
||||||
$container->run();
|
$container->run();
|
||||||
|
die(123);
|
||||||
|
|
||||||
$pdo = new \PDO(
|
$pdo = new \PDO(
|
||||||
sprintf('mysql:host=%s;port=3306', $container->getAddress()),
|
sprintf('mysql:host=%s;port=3306', $container->getAddress()),
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Testcontainers\Tests\Integration;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Testcontainers\Container\GenericContainer;
|
||||||
|
use Testcontainers\Registry;
|
||||||
|
|
||||||
|
class RedisContainerTest extends TestCase
|
||||||
|
{
|
||||||
|
// public static function tearDownAfterClass(): void
|
||||||
|
// {
|
||||||
|
// parent::tearDownAfterClass();
|
||||||
|
//
|
||||||
|
// Registry::cleanup();
|
||||||
|
// }
|
||||||
|
|
||||||
|
public function testRedisContainer(): void
|
||||||
|
{
|
||||||
|
$redisContainer = (new GenericContainer('redis:alpine'))
|
||||||
|
->withExposedPorts(6379)
|
||||||
|
->start();
|
||||||
|
|
||||||
|
$redisClient = new \Predis\Client([
|
||||||
|
'host' => 'localhost',
|
||||||
|
'port' => 6379,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$redisClient->set('greetings', 'Hello, World!');
|
||||||
|
|
||||||
|
$this->assertEquals('Hello, World!', $redisClient->get('greetings'));
|
||||||
|
$redisContainer->remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user