mirror of
https://github.com/stan220/testcontainers-php.git
synced 2026-09-12 11:29:36 +00:00
added beluga-php/docker-php client and some basic updates to the base Container class
This commit is contained in:
+4
-2
@@ -15,7 +15,8 @@
|
|||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">= 8.1",
|
"php": ">= 8.1",
|
||||||
"symfony/process": "^5.0|^6.0|^7.0"
|
"beluga-php/docker-php": "^1.45",
|
||||||
|
"symfony/http-client": "^7.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
@@ -44,7 +45,8 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
"phpstan/extension-installer": true
|
"phpstan/extension-installer": true,
|
||||||
|
"php-http/discovery": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+92
-94
@@ -4,11 +4,17 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Testcontainers\Container;
|
namespace Testcontainers\Container;
|
||||||
|
|
||||||
use Symfony\Component\Process\Process;
|
use Docker\API\Model\ContainersCreatePostBody;
|
||||||
|
use Docker\API\Model\ContainersIdExecPostBody;
|
||||||
|
use Docker\API\Model\EndpointSettings;
|
||||||
|
use Docker\API\Model\HealthConfig;
|
||||||
|
use Docker\API\Model\Mount;
|
||||||
|
use Docker\API\Model\NetworkingConfig;
|
||||||
|
use Docker\API\Model\Port;
|
||||||
|
use Docker\Docker;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Testcontainers\Exception\ContainerNotReadyException;
|
use Testcontainers\Exception\ContainerNotReadyException;
|
||||||
use Testcontainers\Registry;
|
use Testcontainers\Registry;
|
||||||
use Testcontainers\Trait\DockerContainerAwareTrait;
|
|
||||||
use Testcontainers\Wait\WaitForNothing;
|
|
||||||
use Testcontainers\Wait\WaitInterface;
|
use Testcontainers\Wait\WaitInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,43 +25,44 @@ use Testcontainers\Wait\WaitInterface;
|
|||||||
*/
|
*/
|
||||||
class Container
|
class Container
|
||||||
{
|
{
|
||||||
use DockerContainerAwareTrait;
|
protected Docker $dockerClient;
|
||||||
|
|
||||||
private string $id;
|
protected ContainersCreatePostBody $containerConfig;
|
||||||
|
|
||||||
private ?string $entryPoint = null;
|
protected string $image;
|
||||||
|
|
||||||
|
protected string $containerName;
|
||||||
|
|
||||||
|
protected string $id;
|
||||||
|
|
||||||
|
protected ?string $entryPoint = null;
|
||||||
|
|
||||||
|
protected ?HealthConfig $healthConfig = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string, string>
|
* @var array<string, string>
|
||||||
*/
|
*/
|
||||||
private array $env = [];
|
protected array $env = [];
|
||||||
|
|
||||||
private Process $process;
|
protected WaitInterface $wait;
|
||||||
private WaitInterface $wait;
|
|
||||||
|
|
||||||
private bool $privileged = false;
|
protected bool $privileged = false;
|
||||||
private ?string $network = null;
|
protected ?string $networkName = null;
|
||||||
private ?string $healthCheckCommand = null;
|
|
||||||
private int $healthCheckIntervalInMS;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ContainerInspect
|
* @var array<Mount>
|
||||||
*/
|
*/
|
||||||
private array $inspectedData;
|
protected array $mounts = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string>
|
* @var array<Port>
|
||||||
*/
|
*/
|
||||||
private array $mounts = [];
|
protected array $ports = [];
|
||||||
|
|
||||||
/**
|
protected function __construct(string $image)
|
||||||
* @var array<string>
|
|
||||||
*/
|
|
||||||
private array $ports = [];
|
|
||||||
|
|
||||||
protected function __construct(private string $image)
|
|
||||||
{
|
{
|
||||||
$this->wait = new WaitForNothing();
|
$this->image = $image;
|
||||||
|
$this->dockerClient = Docker::create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function make(string $image): self
|
public static function make(string $image): self
|
||||||
@@ -98,24 +105,24 @@ class Container
|
|||||||
|
|
||||||
public function withHealthCheckCommand(string $command, int $healthCheckIntervalInMS = 1000): self
|
public function withHealthCheckCommand(string $command, int $healthCheckIntervalInMS = 1000): self
|
||||||
{
|
{
|
||||||
$this->healthCheckCommand = $command;
|
$this->healthConfig = new HealthConfig([
|
||||||
$this->healthCheckIntervalInMS = $healthCheckIntervalInMS;
|
'Test' => ['CMD', $command],
|
||||||
|
'Interval' => $healthCheckIntervalInMS,
|
||||||
|
]);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withMount(string $localPath, string $containerPath): self
|
public function withMount(string $localPath, string $containerPath): self
|
||||||
{
|
{
|
||||||
$this->mounts[] = '-v';
|
$this->mounts[] = new Mount(['type' => 'bind', 'source' => $localPath, 'target' => $containerPath]);
|
||||||
$this->mounts[] = sprintf('%s:%s', $localPath, $containerPath);
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withPort(string $localPort, string $containerPort): self
|
public function withPort(string $localPort, string $containerPort): self
|
||||||
{
|
{
|
||||||
$this->ports[] = '-p';
|
$this->ports[] = new Port(['privatePort' => (int) $containerPort, 'publicPort' => (int) $localPort]);
|
||||||
$this->ports[] = sprintf('%s:%s', $localPort, $containerPort);
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -127,60 +134,52 @@ class Container
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withNetwork(string $network): self
|
public function withNetwork(string $networkName): self
|
||||||
{
|
{
|
||||||
$this->network = $network;
|
$this->networkName = $networkName;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(bool $wait = true): self
|
public function run(bool $wait = true): self
|
||||||
{
|
{
|
||||||
$this->id = uniqid('testcontainer', true);
|
$this->containerName = uniqid('testcontainer', true);
|
||||||
|
|
||||||
$params = [
|
$this->containerConfig = new ContainersCreatePostBody();
|
||||||
'docker',
|
$this->containerConfig->setImage($this->image);
|
||||||
'run',
|
|
||||||
'--rm',
|
|
||||||
'--detach',
|
|
||||||
'--name',
|
|
||||||
$this->id,
|
|
||||||
...$this->mounts,
|
|
||||||
...$this->ports,
|
|
||||||
];
|
|
||||||
|
|
||||||
|
$envs = [];
|
||||||
foreach ($this->env as $name => $value) {
|
foreach ($this->env as $name => $value) {
|
||||||
$params[] = '--env';
|
$envs[] = $name . '=' . $value;
|
||||||
$params[] = $name . '=' . $value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->healthCheckCommand !== null) {
|
$this->containerConfig->setEnv($envs);
|
||||||
$params[] = '--health-cmd';
|
|
||||||
$params[] = $this->healthCheckCommand;
|
if ($this->healthConfig !== null) {
|
||||||
$params[] = '--health-interval';
|
$this->containerConfig->setHealthcheck($this->healthConfig);
|
||||||
$params[] = $this->healthCheckIntervalInMS . 'ms';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->network !== null) {
|
if ($this->networkName !== null) {
|
||||||
$params[] = '--network';
|
$this->containerConfig->setNetworkingConfig(new NetworkingConfig([
|
||||||
$params[] = $this->network;
|
'endpointsConfig' => [
|
||||||
|
$this->networkName => new EndpointSettings([
|
||||||
|
'aliases' => [$this->containerName],
|
||||||
|
'networkID' => $this->networkName,
|
||||||
|
]),
|
||||||
|
]]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->entryPoint !== null) {
|
if ($this->entryPoint !== null) {
|
||||||
$params[] = '--entrypoint';
|
$this->containerConfig->setEntrypoint([$this->entryPoint]);
|
||||||
$params[] = $this->entryPoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->privileged) {
|
if ($this->privileged) {
|
||||||
$params[] = '--privileged';
|
//TODO: Implement privileged mode
|
||||||
}
|
}
|
||||||
|
|
||||||
$params[] = $this->image;
|
$containerCreateResponse = $this->dockerClient->containerCreate($this->containerConfig, ['name' => $this->containerName]);
|
||||||
|
|
||||||
$this->process = new Process($params);
|
$this->id = $containerCreateResponse->getId();
|
||||||
$this->process->mustRun();
|
|
||||||
|
|
||||||
$this->inspectedData = self::dockerContainerInspect($this->id);
|
|
||||||
|
|
||||||
Registry::add($this);
|
Registry::add($this);
|
||||||
|
|
||||||
@@ -193,46 +192,45 @@ class Container
|
|||||||
|
|
||||||
public function wait(int $wait = 100): self
|
public function wait(int $wait = 100): self
|
||||||
{
|
{
|
||||||
for ($i = 0; $i < $wait; $i++) {
|
|
||||||
try {
|
|
||||||
$this->wait->wait($this->id);
|
|
||||||
return $this;
|
|
||||||
} catch (ContainerNotReadyException $e) {
|
|
||||||
usleep(500000);
|
usleep(500000);
|
||||||
}
|
return $this;
|
||||||
}
|
|
||||||
|
|
||||||
throw new ContainerNotReadyException($this->id);
|
// 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
|
||||||
{
|
{
|
||||||
$stop = new Process(['docker', 'stop', $this->id]);
|
$this->dockerClient->containerStop($this->id);
|
||||||
$stop->mustRun();
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function start(): self
|
public function start(): self
|
||||||
{
|
{
|
||||||
$start = new Process(['docker', 'start', $this->id]);
|
$this->dockerClient->containerStart($this->id);
|
||||||
$start->mustRun();
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function restart(): self
|
public function restart(): self
|
||||||
{
|
{
|
||||||
$restart = new Process(['docker', 'restart', $this->id]);
|
$this->dockerClient->containerRestart($this->id);
|
||||||
$restart->mustRun();
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function remove(): self
|
public function remove(): self
|
||||||
{
|
{
|
||||||
$remove = new Process(['docker', 'rm', '-f', $this->id]);
|
$this->dockerClient->containerDelete($this->id);
|
||||||
$remove->mustRun();
|
|
||||||
|
|
||||||
Registry::remove($this);
|
Registry::remove($this);
|
||||||
|
|
||||||
@@ -241,37 +239,37 @@ class Container
|
|||||||
|
|
||||||
public function kill(): self
|
public function kill(): self
|
||||||
{
|
{
|
||||||
$kill = new Process(['docker', 'kill', $this->id]);
|
$this->dockerClient->containerKill($this->id);
|
||||||
$kill->mustRun();
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string> $command
|
* @param array<string> $commandAsArray
|
||||||
*/
|
*/
|
||||||
public function execute(array $command): Process
|
public function execute(array $commandAsArray): ResponseInterface
|
||||||
{
|
{
|
||||||
$process = new Process(['docker', 'exec', $this->id, ...$command]);
|
$command = new ContainersIdExecPostBody();
|
||||||
$process->mustRun();
|
$command->setCmd($commandAsArray);
|
||||||
|
return $this->dockerClient->containerExec($this->id, $command);
|
||||||
return $process;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function logs(): string
|
public function logs(): string
|
||||||
{
|
{
|
||||||
$logs = new Process(['docker', 'logs', $this->id]);
|
return $this->dockerClient->containerLogs($this->id)?->getBody()?->getContents() ?? '';
|
||||||
$logs->mustRun();
|
|
||||||
|
|
||||||
return $logs->getOutput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAddress(): string
|
public function getAddress(): string
|
||||||
{
|
{
|
||||||
return self::dockerContainerAddress(
|
$containerNetworks = $this->dockerClient->containerInspect($this->id)
|
||||||
containerId: $this->id,
|
->getNetworkSettings()->getNetworks();
|
||||||
networkName: $this->network,
|
$containerAddress = '';
|
||||||
inspectedData: $this->inspectedData
|
foreach ($containerNetworks as $network) {
|
||||||
);
|
if($network->getNetworkID() === $this->id) {
|
||||||
|
$containerAddress = $network->getIpAddress();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $containerAddress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-13
@@ -5,31 +5,35 @@ declare(strict_types=1);
|
|||||||
namespace Testcontainers\Wait;
|
namespace Testcontainers\Wait;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Symfony\Component\Process\Process;
|
use Docker\API\Model\ContainersIdExecPostBody;
|
||||||
|
use Docker\API\Model\ExecIdStartPostBody;
|
||||||
|
use Docker\Docker;
|
||||||
use Testcontainers\Exception\ContainerNotReadyException;
|
use Testcontainers\Exception\ContainerNotReadyException;
|
||||||
|
|
||||||
class WaitForExec implements WaitInterface
|
class WaitForExec implements WaitInterface
|
||||||
{
|
{
|
||||||
|
protected Docker $dockerClient;
|
||||||
|
|
||||||
|
protected ContainersIdExecPostBody $execConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string> $command
|
* @param array<string> $command
|
||||||
*/
|
*/
|
||||||
public function __construct(private array $command, private ?Closure $checkFunction = null)
|
public function __construct(private array $command, private ?Closure $checkFunction = null)
|
||||||
{
|
{
|
||||||
|
$this->dockerClient = Docker::create();
|
||||||
|
$execConfig = new ContainersIdExecPostBody();
|
||||||
|
$execConfig->setTty(true);
|
||||||
|
$execConfig->setAttachStdout(true);
|
||||||
|
$execConfig->setAttachStderr(true);
|
||||||
|
$execConfig->setCmd($this->command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function wait(string $id): void
|
public function wait(string $id): void
|
||||||
{
|
{
|
||||||
$process = new Process(['docker', 'exec', $id, ...$this->command]);
|
$execid = $this->dockerClient->containerExec($id, $this->execConfig)->getId() ?? '';
|
||||||
|
$execStartConfig = new ExecIdStartPostBody();
|
||||||
try {
|
$execStartConfig->setDetach(false);
|
||||||
$process->mustRun();
|
$this->dockerClient->execStart($execid, $execStartConfig);
|
||||||
} catch (\Exception $e) {
|
|
||||||
throw new ContainerNotReadyException($id, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->checkFunction !== null) {
|
|
||||||
$func = $this->checkFunction;
|
|
||||||
$func($process);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,22 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Testcontainers\Wait;
|
namespace Testcontainers\Wait;
|
||||||
|
|
||||||
use RuntimeException;
|
use Docker\Docker;
|
||||||
use Symfony\Component\Process\Process;
|
|
||||||
use Testcontainers\Exception\ContainerNotReadyException;
|
use Testcontainers\Exception\ContainerNotReadyException;
|
||||||
|
|
||||||
class WaitForHealthCheck implements WaitInterface
|
class WaitForHealthCheck implements WaitInterface
|
||||||
{
|
{
|
||||||
|
protected Docker $dockerClient;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->dockerClient = Docker::create();
|
||||||
|
}
|
||||||
public function wait(string $id): void
|
public function wait(string $id): void
|
||||||
{
|
{
|
||||||
$process = new Process(['docker', 'inspect', '--format', '{{json .State.Health.Status}}', $id]);
|
$containerInspect = $this->dockerClient->containerInspect($id);
|
||||||
$process->mustRun();
|
$containerInspect->getBody()->getContents();
|
||||||
|
dd($containerInspect->getStatusCode());
|
||||||
$status = json_decode($process->getOutput(), true, 512, JSON_THROW_ON_ERROR);
|
|
||||||
|
|
||||||
if (!is_string($status)) {
|
|
||||||
throw new ContainerNotReadyException($id, new RuntimeException('Invalid json output'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = trim($status, '"');
|
|
||||||
|
|
||||||
if ($status !== 'healthy') {
|
if ($status !== 'healthy') {
|
||||||
throw new ContainerNotReadyException($id);
|
throw new ContainerNotReadyException($id);
|
||||||
|
|||||||
@@ -4,13 +4,11 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Testcontainers\Wait;
|
namespace Testcontainers\Wait;
|
||||||
|
|
||||||
|
use Docker\Docker;
|
||||||
use Testcontainers\Exception\ContainerNotReadyException;
|
use Testcontainers\Exception\ContainerNotReadyException;
|
||||||
use Testcontainers\Trait\DockerContainerAwareTrait;
|
|
||||||
|
|
||||||
class WaitForHttp implements WaitInterface
|
class WaitForHttp implements WaitInterface
|
||||||
{
|
{
|
||||||
use DockerContainerAwareTrait;
|
|
||||||
|
|
||||||
public const METHOD_GET = 'GET';
|
public const METHOD_GET = 'GET';
|
||||||
public const METHOD_POST = 'POST';
|
public const METHOD_POST = 'POST';
|
||||||
public const METHOD_PUT = 'PUT';
|
public const METHOD_PUT = 'PUT';
|
||||||
@@ -22,9 +20,11 @@ class WaitForHttp implements WaitInterface
|
|||||||
private string $method = 'GET';
|
private string $method = 'GET';
|
||||||
private string $path = '/';
|
private string $path = '/';
|
||||||
private int $statusCode = 200;
|
private int $statusCode = 200;
|
||||||
|
private Docker $dockerClient;
|
||||||
|
|
||||||
public function __construct(private int $port)
|
public function __construct(private int $port)
|
||||||
{
|
{
|
||||||
|
$this->dockerClient = Docker::create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function make(int $port): self
|
public static function make(int $port): self
|
||||||
@@ -58,7 +58,14 @@ class WaitForHttp implements WaitInterface
|
|||||||
|
|
||||||
public function wait(string $id): void
|
public function wait(string $id): void
|
||||||
{
|
{
|
||||||
$containerAddress = self::dockerContainerAddress(containerId: $id);
|
$containerNetworks = $this->dockerClient->containerInspect($id)->getNetworkSettings()->getNetworks();
|
||||||
|
$containerAddress = null;
|
||||||
|
foreach ($containerNetworks as $network) {
|
||||||
|
if($network->getNetworkID() === $id) {
|
||||||
|
$containerAddress = $network->getIpAddress();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$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,21 +4,24 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Testcontainers\Wait;
|
namespace Testcontainers\Wait;
|
||||||
|
|
||||||
|
use Docker\Docker;
|
||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\Process\Process;
|
||||||
use Testcontainers\Exception\ContainerNotReadyException;
|
use Testcontainers\Exception\ContainerNotReadyException;
|
||||||
|
|
||||||
class WaitForLog implements WaitInterface
|
class WaitForLog implements WaitInterface
|
||||||
{
|
{
|
||||||
|
protected Docker $dockerClient;
|
||||||
|
|
||||||
public function __construct(private string $message, private bool $enableRegex = false)
|
public function __construct(private string $message, private bool $enableRegex = false)
|
||||||
{
|
{
|
||||||
|
$this->dockerClient = Docker::create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function wait(string $id): void
|
public function wait(string $id): void
|
||||||
{
|
{
|
||||||
$process = new Process(['docker', 'logs', $id]);
|
$logs = $this->dockerClient->containerLogs($id);
|
||||||
$process->mustRun();
|
|
||||||
|
|
||||||
$output = $process->getOutput() . PHP_EOL . $process->getErrorOutput();
|
$output = $logs->getBody()->getContents();
|
||||||
|
|
||||||
if ($this->enableRegex) {
|
if ($this->enableRegex) {
|
||||||
if (!preg_match($this->message, $output)) {
|
if (!preg_match($this->message, $output)) {
|
||||||
|
|||||||
@@ -4,17 +4,18 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Testcontainers\Wait;
|
namespace Testcontainers\Wait;
|
||||||
|
|
||||||
|
use Docker\Docker;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Testcontainers\Exception\ContainerNotReadyException;
|
use Testcontainers\Exception\ContainerNotReadyException;
|
||||||
use Testcontainers\Trait\DockerContainerAwareTrait;
|
|
||||||
|
|
||||||
final class WaitForTcpPortOpen implements WaitInterface
|
final class WaitForTcpPortOpen implements WaitInterface
|
||||||
{
|
{
|
||||||
use DockerContainerAwareTrait;
|
private Docker $dockerClient;
|
||||||
|
|
||||||
public function __construct(private readonly int $port, private readonly ?string $network = null)
|
public function __construct(private readonly int $port, private readonly ?string $network = null)
|
||||||
{
|
{
|
||||||
|
$this->dockerClient = Docker::create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function make(int $port, ?string $network = null): self
|
public static function make(int $port, ?string $network = null): self
|
||||||
@@ -27,7 +28,16 @@ final class WaitForTcpPortOpen implements WaitInterface
|
|||||||
*/
|
*/
|
||||||
public function wait(string $id): void
|
public function wait(string $id): void
|
||||||
{
|
{
|
||||||
if (@fsockopen(self::dockerContainerAddress(containerId: $id, networkName: $this->network), $this->port) === false) {
|
$containerInspectResult = $this->dockerClient->containerInspect($id);
|
||||||
|
$dockerContainerNetworks = $containerInspectResult->getNetworkSettings()->getNetworks();
|
||||||
|
$dockerContainerAddress = '';
|
||||||
|
foreach ($dockerContainerNetworks as $network) {
|
||||||
|
if ($network->getNetworkID() === $this->network) {
|
||||||
|
$dockerContainerAddress = $network->getIPAddress();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (@fsockopen($dockerContainerAddress, $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'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ use Testcontainers\Wait\WaitForTcpPortOpen;
|
|||||||
|
|
||||||
class WaitStrategyTest extends TestCase
|
class WaitStrategyTest extends TestCase
|
||||||
{
|
{
|
||||||
use DockerContainerAwareTrait;
|
|
||||||
|
|
||||||
public static function tearDownAfterClass(): void
|
public static function tearDownAfterClass(): void
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user