Code reuse on working with docker networks

This commit is contained in:
Nikola Petkanski
2024-07-16 05:07:39 +03:00
parent ed18c0b8f8
commit 11c3e21e92
5 changed files with 73 additions and 14 deletions
+3 -2
View File
@@ -15,6 +15,7 @@ use Testcontainer\Wait\WaitInterface;
* @phpstan-type ContainerInspectSingleNetwork array<int, array{'NetworkSettings': array{'IPAddress': string}}> * @phpstan-type ContainerInspectSingleNetwork array<int, array{'NetworkSettings': array{'IPAddress': string}}>
* @phpstan-type ContainerInspectMultipleNetworks array<int, array{'NetworkSettings': array{'Networks': array<string, array{'IPAddress': string}>}}> * @phpstan-type ContainerInspectMultipleNetworks array<int, array{'NetworkSettings': array{'Networks': array<string, array{'IPAddress': string}>}}>
* @phpstan-type ContainerInspect ContainerInspectSingleNetwork|ContainerInspectMultipleNetworks * @phpstan-type ContainerInspect ContainerInspectSingleNetwork|ContainerInspectMultipleNetworks
* @phpstan-type DockerNetwork array{CreatedAt: string, Driver: string, ID: string, IPv6: string, Internal: string, Labels: string, Name: string, Scope: string}
*/ */
class Container class Container
{ {
@@ -167,7 +168,7 @@ class Container
$this->process = new Process($params); $this->process = new Process($params);
$this->process->mustRun(); $this->process->mustRun();
$this->inspectedData = $this->getContainerInspect($this->id); $this->inspectedData = self::dockerContainerInspect($this->id);
Registry::add($this); Registry::add($this);
@@ -255,7 +256,7 @@ class Container
public function getAddress(): string public function getAddress(): string
{ {
return $this->getContainerAddress( return self::dockerContainerAddress(
containerId: $this->id, containerId: $this->id,
networkName: $this->network, networkName: $this->network,
inspectedData: $this->inspectedData inspectedData: $this->inspectedData
+51 -4
View File
@@ -6,10 +6,12 @@ namespace Testcontainer\Trait;
use JsonException; use JsonException;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
use Testcontainer\Container\Container;
use UnexpectedValueException; use UnexpectedValueException;
/** /**
* @phpstan-import-type ContainerInspect from \Testcontainer\Container\Container * @phpstan-import-type ContainerInspect from Container
* @phpstan-import-type DockerNetwork from Container
*/ */
trait DockerContainerAwareTrait trait DockerContainerAwareTrait
{ {
@@ -21,10 +23,10 @@ trait DockerContainerAwareTrait
* *
* @throws JsonException * @throws JsonException
*/ */
protected function getContainerAddress(string $containerId, ?string $networkName = null, ?array $inspectedData = null): string private static function dockerContainerAddress(string $containerId, ?string $networkName = null, ?array $inspectedData = null): string
{ {
if (! is_array($inspectedData)) { if (! is_array($inspectedData)) {
$inspectedData = $this->getContainerInspect($containerId); $inspectedData = self::dockerContainerInspect($containerId);
} }
if (is_string($networkName)) { if (is_string($networkName)) {
@@ -50,7 +52,7 @@ trait DockerContainerAwareTrait
* *
* @throws JsonException * @throws JsonException
*/ */
protected function getContainerInspect(string $containerId): array private static function dockerContainerInspect(string $containerId): array
{ {
$process = new Process(['docker', 'inspect', $containerId]); $process = new Process(['docker', 'inspect', $containerId]);
$process->mustRun(); $process->mustRun();
@@ -58,4 +60,49 @@ trait DockerContainerAwareTrait
/** @var ContainerInspect */ /** @var ContainerInspect */
return json_decode($process->getOutput(), true, 512, JSON_THROW_ON_ERROR); 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();
}
} }
+1 -1
View File
@@ -58,7 +58,7 @@ class WaitForHttp implements WaitInterface
public function wait(string $id): void public function wait(string $id): void
{ {
$containerAddress = $this->getContainerAddress(containerId: $id); $containerAddress = self::dockerContainerAddress(containerId: $id);
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, sprintf('http://%s:%d%s', $containerAddress, $this->port, $this->path)); curl_setopt($ch, CURLOPT_URL, sprintf('http://%s:%d%s', $containerAddress, $this->port, $this->path));
+4 -4
View File
@@ -13,13 +13,13 @@ final class WaitForTcpPortOpen implements WaitInterface
{ {
use DockerContainerAwareTrait; use DockerContainerAwareTrait;
public function __construct(private readonly int $port) public function __construct(private readonly int $port, private readonly ?string $network = null)
{ {
} }
public static function make(int $port): self public static function make(int $port, ?string $network = null): self
{ {
return new self($port); return new self($port, $network);
} }
/** /**
@@ -27,7 +27,7 @@ final class WaitForTcpPortOpen implements WaitInterface
*/ */
public function wait(string $id): void public function wait(string $id): void
{ {
if (@fsockopen($this->getContainerAddress($id), $this->port) === false) { if (@fsockopen(self::dockerContainerAddress(containerId: $id, networkName: $this->network), $this->port) === false) {
throw new ContainerNotReadyException($id, new RuntimeException('Unable to connect to container TCP port')); throw new ContainerNotReadyException($id, new RuntimeException('Unable to connect to container TCP port'));
} }
} }
+14 -3
View File
@@ -10,6 +10,8 @@ use Predis\Connection\ConnectionException;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
use Testcontainer\Container\Container; use Testcontainer\Container\Container;
use Testcontainer\Exception\ContainerNotReadyException; use Testcontainer\Exception\ContainerNotReadyException;
use Testcontainer\Registry;
use Testcontainer\Trait\DockerContainerAwareTrait;
use Testcontainer\Wait\WaitForExec; use Testcontainer\Wait\WaitForExec;
use Testcontainer\Wait\WaitForHealthCheck; use Testcontainer\Wait\WaitForHealthCheck;
use Testcontainer\Wait\WaitForHttp; use Testcontainer\Wait\WaitForHttp;
@@ -18,6 +20,15 @@ use Testcontainer\Wait\WaitForTcpPortOpen;
class WaitStrategyTest extends TestCase class WaitStrategyTest extends TestCase
{ {
use DockerContainerAwareTrait;
public static function tearDownAfterClass(): void
{
parent::tearDownAfterClass();
Registry::cleanup();
}
public function testWaitForExec(): void public function testWaitForExec(): void
{ {
$called = false; $called = false;
@@ -95,17 +106,17 @@ class WaitStrategyTest extends TestCase
/** /**
* @dataProvider provideWaitForTcpPortOpen * @dataProvider provideWaitForTcpPortOpen
*/ */
public function testWaitForTcpPortOpen(bool $canConnect): void public function testWaitForTcpPortOpen(bool $wait): void
{ {
$container = Container::make('nginx:alpine'); $container = Container::make('nginx:alpine');
if ($canConnect) { if ($wait) {
$container->withWait(WaitForTcpPortOpen::make(80)); $container->withWait(WaitForTcpPortOpen::make(80));
} }
$container->run(); $container->run();
if ($canConnect) { if ($wait) {
static::assertIsResource(fsockopen($container->getAddress(), 80), 'Failed to connect to container'); static::assertIsResource(fsockopen($container->getAddress(), 80), 'Failed to connect to container');
return; return;
} }