mirror of
https://github.com/stan220/testcontainers-php.git
synced 2026-09-12 14:29:35 +00:00
adjust wait for host port strategy
This commit is contained in:
@@ -108,7 +108,7 @@ class StartedGenericContainer implements StartedTestContainer
|
|||||||
|
|
||||||
public function getMappedPort(int $port): int
|
public function getMappedPort(int $port): int
|
||||||
{
|
{
|
||||||
$ports = (array) $this->ports();
|
$ports = (array) $this->getBoundPorts();
|
||||||
/** @var PortBinding | null $portBinding */
|
/** @var PortBinding | null $portBinding */
|
||||||
$portBinding = $ports["{$port}/tcp"][0] ?? null;
|
$portBinding = $ports["{$port}/tcp"][0] ?? null;
|
||||||
$mappedPort = $portBinding?->getHostPort();
|
$mappedPort = $portBinding?->getHostPort();
|
||||||
@@ -121,7 +121,7 @@ class StartedGenericContainer implements StartedTestContainer
|
|||||||
|
|
||||||
public function getFirstMappedPort(): int
|
public function getFirstMappedPort(): int
|
||||||
{
|
{
|
||||||
$ports = (array) $this->ports();
|
$ports = (array) $this->getBoundPorts();
|
||||||
$port = array_key_first($ports);
|
$port = array_key_first($ports);
|
||||||
/** @var PortBinding | null $firstPortBinding */
|
/** @var PortBinding | null $firstPortBinding */
|
||||||
$firstPortBinding = $ports[$port][0] ?? null;
|
$firstPortBinding = $ports[$port][0] ?? null;
|
||||||
@@ -193,10 +193,10 @@ class StartedGenericContainer implements StartedTestContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, array<PortBinding>>
|
* @return iterable<string, array<PortBinding>>
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
protected function ports(): iterable
|
public function getBoundPorts(): iterable
|
||||||
{
|
{
|
||||||
$ports = $this->inspect()?->getNetworkSettings()?->getPorts();
|
$ports = $this->inspect()?->getNetworkSettings()?->getPorts();
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Testcontainers\Container;
|
namespace Testcontainers\Container;
|
||||||
|
|
||||||
|
use Docker\API\Model\PortBinding;
|
||||||
use Docker\Docker;
|
use Docker\Docker;
|
||||||
|
|
||||||
interface StartedTestContainer
|
interface StartedTestContainer
|
||||||
@@ -13,6 +14,11 @@ interface StartedTestContainer
|
|||||||
*/
|
*/
|
||||||
public function exec(array $command): string;
|
public function exec(array $command): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return iterable<string, array<PortBinding>>
|
||||||
|
*/
|
||||||
|
public function getBoundPorts(): iterable;
|
||||||
|
|
||||||
public function getClient(): Docker;
|
public function getClient(): Docker;
|
||||||
|
|
||||||
public function getFirstMappedPort(): int;
|
public function getFirstMappedPort(): int;
|
||||||
|
|||||||
@@ -9,18 +9,9 @@ use Testcontainers\Exception\ContainerWaitingTimeoutException;
|
|||||||
|
|
||||||
class WaitForHostPort extends BaseWaitStrategy
|
class WaitForHostPort extends BaseWaitStrategy
|
||||||
{
|
{
|
||||||
public function __construct(
|
|
||||||
protected int $port,
|
|
||||||
int $timeout = 10000,
|
|
||||||
int $pollInterval = 500
|
|
||||||
) {
|
|
||||||
parent::__construct($timeout, $pollInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function wait(StartedTestContainer $container): void
|
public function wait(StartedTestContainer $container): void
|
||||||
{
|
{
|
||||||
$startTime = microtime(true) * 1000;
|
$startTime = microtime(true) * 1000;
|
||||||
$containerAddress = $container->getHost();
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
$elapsedTime = (microtime(true) * 1000) - $startTime;
|
$elapsedTime = (microtime(true) * 1000) - $startTime;
|
||||||
@@ -29,7 +20,7 @@ class WaitForHostPort extends BaseWaitStrategy
|
|||||||
throw new ContainerWaitingTimeoutException($container->getId());
|
throw new ContainerWaitingTimeoutException($container->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isPortOpen($containerAddress, $this->port)) {
|
if ($this->boundPortsOpened($container)) {
|
||||||
return; // Port is open, container is ready
|
return; // Port is open, container is ready
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +28,28 @@ class WaitForHostPort extends BaseWaitStrategy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StartedTestContainer $container
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function boundPortsOpened(StartedTestContainer $container): bool
|
||||||
|
{
|
||||||
|
$boundPorts = $container->getBoundPorts();
|
||||||
|
foreach ($boundPorts as $bindings) {
|
||||||
|
foreach ($bindings as $binding) {
|
||||||
|
$hostIp = trim($binding->getHostIp() ?? '');
|
||||||
|
if ($hostIp === '' || $hostIp === '0.0.0.0') {
|
||||||
|
$hostIp = $container->getHost();
|
||||||
|
}
|
||||||
|
$hostPort = (int)$binding->getHostPort();
|
||||||
|
if (!$this->isPortOpen($hostIp, $hostPort)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private function isPortOpen(string $ipAddress, int $port): bool
|
private function isPortOpen(string $ipAddress, int $port): bool
|
||||||
{
|
{
|
||||||
$connection = @fsockopen($ipAddress, $port, $errno, $errstr, 2);
|
$connection = @fsockopen($ipAddress, $port, $errno, $errstr, 2);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ namespace Testcontainers\Tests\Integration;
|
|||||||
use Docker\API\Model\ContainersIdJsonGetResponse200;
|
use Docker\API\Model\ContainersIdJsonGetResponse200;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Testcontainers\Container\GenericContainer;
|
use Testcontainers\Container\GenericContainer;
|
||||||
use Testcontainers\Utils\PortGenerator\FixedPortGenerator;
|
|
||||||
use Testcontainers\Wait\WaitForHostPort;
|
use Testcontainers\Wait\WaitForHostPort;
|
||||||
|
|
||||||
class GenericContainerTest extends TestCase
|
class GenericContainerTest extends TestCase
|
||||||
@@ -115,13 +114,12 @@ class GenericContainerTest extends TestCase
|
|||||||
public function testShouldReturnFirstMappedPort(): void
|
public function testShouldReturnFirstMappedPort(): void
|
||||||
{
|
{
|
||||||
$container = (new GenericContainer('nginx'))
|
$container = (new GenericContainer('nginx'))
|
||||||
->withPortGenerator(new FixedPortGenerator([9090]))
|
|
||||||
->withExposedPorts(80)
|
->withExposedPorts(80)
|
||||||
->withWait(new WaitForHostPort(9090))
|
->withWait(new WaitForHostPort())
|
||||||
->start();
|
->start();
|
||||||
$firstMappedPort = $container->getFirstMappedPort();
|
$firstMappedPort = $container->getFirstMappedPort();
|
||||||
|
|
||||||
self::assertSame($firstMappedPort, 9090, 'First mapped port does not match 9090');
|
self::assertSame($firstMappedPort, $container->getMappedPort(80));
|
||||||
|
|
||||||
$container->stop();
|
$container->stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace Testcontainers\Tests\Integration;
|
namespace Testcontainers\Tests\Integration;
|
||||||
|
|
||||||
use Testcontainers\Container\GenericContainer;
|
use Testcontainers\Container\GenericContainer;
|
||||||
|
use Testcontainers\Wait\WaitForHostPort;
|
||||||
|
|
||||||
class StartedGenericContainerTest extends ContainerTestCase
|
class StartedGenericContainerTest extends ContainerTestCase
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user