Implement WaitForExec using new Client library. Add BaseWait to avoid some code duplication. Adjust Postgres test

This commit is contained in:
Sergei Shitikov
2024-09-01 18:23:21 +02:00
parent 3dd4d2525a
commit ea07a96091
6 changed files with 94 additions and 45 deletions
+15 -5
View File
@@ -8,11 +8,18 @@ use Testcontainers\Wait\WaitForExec;
class PostgresContainer extends GenericContainer
{
public function __construct(string $version = 'latest', string $rootPassword = 'root')
{
public function __construct(
string $version = 'latest',
public readonly string $username = 'test',
public readonly string $password = 'test',
public readonly string $database = 'test'
) {
parent::__construct('postgres:' . $version);
$this->withEnvironment('POSTGRES_PASSWORD', $rootPassword);
$this->withWait(new WaitForExec(["pg_isready", "-h", "127.0.0.1"]));
$this->withExposedPorts(5432);
$this->withEnvironment('POSTGRES_USER', $this->username);
$this->withEnvironment('POSTGRES_PASSWORD', $this->password);
$this->withEnvironment('POSTGRES_DB', $this->database);
$this->withWait(new WaitForExec(["pg_isready", "-h", "127.0.0.1", "-U", $this->username]));
}
/**
@@ -21,7 +28,10 @@ class PostgresContainer extends GenericContainer
*/
public static function make(string $version = 'latest', string $dbPassword = 'root'): self
{
return new self($version, $dbPassword);
return new self(
version: $version,
password: $dbPassword
);
}
public function withPostgresUser(string $username): self
+20
View File
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Testcontainers\Wait;
use Docker\Docker;
use Testcontainers\ContainerRuntime\ContainerRuntimeClient;
abstract class BaseWait implements WaitInterface
{
protected Docker $dockerClient;
public function __construct(protected int $timeout = 10000, protected int $pollInterval = 500)
{
$this->dockerClient = ContainerRuntimeClient::getDockerClient();
}
abstract public function wait(string $id): void;
}
+1 -12
View File
@@ -5,23 +5,14 @@ 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
class WaitForContainerRunning extends BaseWait
{
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;
@@ -41,8 +32,6 @@ class WaitForContainerRunning implements WaitInterface
return;
}
var_dump($containerStatus);
usleep($this->pollInterval * 1000);
}
}
+50 -17
View File
@@ -5,34 +5,67 @@ declare(strict_types=1);
namespace Testcontainers\Wait;
use Closure;
use Docker\API\Client;
use Docker\API\Model\ContainersIdExecPostBody;
use Docker\API\Model\ExecIdStartPostBody;
use Docker\Docker;
use Testcontainers\Exception\ContainerWaitingTimeoutException;
class WaitForExec implements WaitInterface
/**
* Uses $timout and $pollInterval in milliseconds to set the parameters for waiting.
*/
class WaitForExec extends BaseWait
{
protected Docker $dockerClient;
protected ContainersIdExecPostBody $execConfig;
/**
* @param array<string> $command
*/
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 __construct(
protected array $command,
protected ?Closure $checkFunction = null,
int $timeout = 10000,
int $pollInterval = 500
) {
parent::__construct($timeout, $pollInterval);
}
public function wait(string $id): void
{
$execid = $this->dockerClient->containerExec($id, $this->execConfig)->getId() ?? '';
$execStartConfig = new ExecIdStartPostBody();
$execStartConfig->setDetach(false);
$this->dockerClient->execStart($execid, $execStartConfig);
$this->execConfig = (new ContainersIdExecPostBody())
->setCmd($this->command)
->setAttachStdout(true)
->setAttachStderr(true);
$startTime = microtime(true) * 1000;
while (true) {
$elapsedTime = (microtime(true) * 1000) - $startTime;
if ($elapsedTime > $this->timeout) {
throw new ContainerWaitingTimeoutException($id);
}
// Create and start the exec command
$exec = $this->dockerClient->containerExec($id, $this->execConfig);
$contents = $this->dockerClient
->execStart($exec->getId(), null, Client::FETCH_RESPONSE)
?->getBody()
->getContents() ?? '';
// Inspect the exec to check the exit code
$execInspect = $this->dockerClient->execInspect($exec->getId());
$exitCode = $execInspect->getExitCode();
// If a custom check function is provided, use it to validate the command output
if ($this->checkFunction !== null) {
$checkResult = ($this->checkFunction)($exitCode, $contents);
if ($checkResult) {
return;
}
} elseif ($exitCode === 0) {
return; // Command succeeded
}
usleep($this->pollInterval * 1000);
}
}
}
+4 -7
View File
@@ -5,23 +5,20 @@ declare(strict_types=1);
namespace Testcontainers\Wait;
use Docker\API\Runtime\Client\Client;
use Docker\Docker;
use Testcontainers\Exception\ContainerWaitingTimeoutException;
/**
* Uses $timout and $pollInterval in milliseconds to set the parameters for waiting.
*/
class WaitForLog implements WaitInterface
class WaitForLog extends BaseWait
{
protected Docker $dockerClient;
public function __construct(
protected string $message,
protected bool $enableRegex = false,
protected int $timeout = 10000,
protected int $pollInterval = 500
int $timeout = 10000,
int $pollInterval = 500
) {
$this->dockerClient = Docker::create();
parent::__construct($timeout, $pollInterval);
}
public function wait(string $id): void
@@ -10,8 +10,8 @@ class PostgreSQLContainerTest extends ContainerTestCase
{
public static function setUpBeforeClass(): void
{
self::$container = (new PostgresContainer('latest', 'test'))
->withPostgresUser('test')
self::$container = (new PostgresContainer())
->withPostgresUser('bar')
->withPostgresDatabase('foo')
->start();
}
@@ -19,8 +19,8 @@ class PostgreSQLContainerTest extends ContainerTestCase
public function testPostgreSQLContainer(): void
{
$pdo = new \PDO(
sprintf('pgsql:host=%s;port=5432;dbname=foo', self::$container->getAddress()),
'test',
'pgsql:host=127.0.0.1;port=5432;dbname=foo',
'bar',
'test',
);