Skip to content

Commit

Permalink
Support for waiting on tcp port to open
Browse files Browse the repository at this point in the history
  • Loading branch information
dinamic committed Jul 15, 2024
1 parent 3ddc75e commit f690a1f
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Container/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ public static function make(string $image): self
return new Container($image);
}

public function getId(): string
{
return $this->id;
}

public function withEntryPoint(string $entryPoint): self
{
$this->entryPoint = $entryPoint;
Expand Down
52 changes: 52 additions & 0 deletions src/Wait/WaitForTcpPortOpen.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace Testcontainer\Wait;

use JsonException;
use RuntimeException;
use Symfony\Component\Process\Process;
use Testcontainer\Exception\ContainerNotReadyException;

class WaitForTcpPortOpen implements WaitInterface
{
public function __construct(protected readonly int $port)
{
}

public static function make(int $port): self
{
return new static($port);

Check failure on line 20 in src/Wait/WaitForTcpPortOpen.php

View workflow job for this annotation

GitHub Actions / build

Unsafe usage of new static().
}

/**
* @throws JsonException
*/
public function wait(string $id): void
{
if (@fsockopen($this->findContainerAddress($id), $this->port) === false) {
throw new ContainerNotReadyException($id, new RuntimeException('Unable to connect to container TCP port'));
}
}

/**
* @throws JsonException
*/
protected function findContainerAddress(string $id): string
{
$process = new Process(['docker', 'inspect', $id]);
$process->mustRun();

/** @var array<int, array{'NetworkSettings': array{'IPAddress': string}}> $data */
$data = json_decode($process->getOutput(), true, 512, JSON_THROW_ON_ERROR);

$containerAddress = $data[0]['NetworkSettings']['IPAddress'] ?? null;

if (! is_string($containerAddress)) {
throw new ContainerNotReadyException($id, new RuntimeException('Unable to find container IP address'));
}

return $containerAddress;
}
}
35 changes: 35 additions & 0 deletions tests/Integration/WaitStrategyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
use Predis\Connection\ConnectionException;
use Symfony\Component\Process\Process;
use Testcontainer\Container\Container;
use Testcontainer\Exception\ContainerNotReadyException;
use Testcontainer\Wait\WaitForExec;
use Testcontainer\Wait\WaitForHealthCheck;
use Testcontainer\Wait\WaitForHttp;
use Testcontainer\Wait\WaitForLog;
use Testcontainer\Wait\WaitForTcpPortOpen;

class WaitStrategyTest extends TestCase
{
Expand Down Expand Up @@ -90,6 +92,39 @@ public function testWaitForHTTP(): void
$this->assertNotEmpty($response);
}

/**
* @dataProvider provideWaitForTcpPortOpen
*/
public function testWaitForTcpPortOpen(bool $canConnect): void
{
$container = Container::make('nginx:alpine');

if ($canConnect) {
$container->withWait(WaitForTcpPortOpen::make(80));
}

$container->run();

if ($canConnect) {
static::assertIsResource(fsockopen($container->getAddress(), 80), 'Failed to connect to container');
return;
}

$containerId = $container->getId();

$this->expectExceptionObject(new ContainerNotReadyException($containerId));

(new WaitForTcpPortOpen(8080))->wait($containerId);
}

public function provideWaitForTcpPortOpen(): array

Check failure on line 120 in tests/Integration/WaitStrategyTest.php

View workflow job for this annotation

GitHub Actions / build

Method Testcontainer\Tests\Integration\WaitStrategyTest::provideWaitForTcpPortOpen() return type has no value type specified in iterable type array.
{
return [
'Can connect to container' => [true],
'Cannot connect to container' => [false],
];
}

public function testWaitForHealthCheck(): void
{
$container = Container::make('nginx')
Expand Down

0 comments on commit f690a1f

Please sign in to comment.