initial commit

This commit is contained in:
Soner Sayakci
2022-10-22 23:59:29 +00:00
commit be3be06681
21 changed files with 4942 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Testcontainer\Wait;
use Closure;
use Symfony\Component\Process\Process;
use Testcontainer\Exception\ContainerNotReadyException;
class WaitForExec implements WaitInterface
{
/**
* @param array<string> $command
*/
public function __construct(private array $command, private ?Closure $checkFunction = null)
{
}
public function wait(string $id): void
{
$process = new Process(['docker', 'exec', $id, ...$this->command]);
try {
$process->mustRun();
} catch (\Exception $e) {
throw new ContainerNotReadyException($id, $e);
}
if ($this->checkFunction !== null) {
$func = $this->checkFunction;
$func($process);
}
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Testcontainer\Wait;
use RuntimeException;
use Symfony\Component\Process\Process;
use Testcontainer\Exception\ContainerNotReadyException;
class WaitForHealthCheck implements WaitInterface
{
public function wait(string $id): void
{
$process = new Process(['docker', 'inspect', '--format', '{{json .State.Health.Status}}', $id]);
$process->mustRun();
$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') {
throw new ContainerNotReadyException($id);
}
}
}
+85
View File
@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Testcontainer\Wait;
use Symfony\Component\Process\Process;
use Testcontainer\Exception\ContainerNotReadyException;
/**
* @phpstan-import-type ContainerInspect from \Testcontainer\Container\Container
*/
class WaitForHttp implements WaitInterface
{
public const METHOD_GET = 'GET';
public const METHOD_POST = 'POST';
public const METHOD_PUT = 'PUT';
public const METHOD_DELETE = 'DELETE';
public const METHOD_HEAD = 'HEAD';
public const METHOD_OPTIONS = 'OPTIONS';
private string $method = 'GET';
private string $path = '/';
private int $statusCode = 200;
public function __construct(private int $port)
{
}
public static function make(int $port): self
{
return new WaitForHttp($port);
}
/**
* @param WaitForHttp::METHOD_* $method
*/
public function withMethod(string $method): self
{
$this->method = $method;
return $this;
}
public function withPath(string $path): self
{
$this->path = $path;
return $this;
}
public function withStatusCode(int $statusCode): self
{
$this->statusCode = $statusCode;
return $this;
}
public function wait(string $id): void
{
$process = new Process(['docker', 'inspect', $id]);
$process->mustRun();
/** @var ContainerInspect $data */
$data = json_decode($process->getOutput(), true);
$ip = $data[0]['NetworkSettings']['IPAddress'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, sprintf('http://%s:%d%s', $ip, $this->port, $this->path));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== $this->statusCode) {
throw new ContainerNotReadyException($id, new \RuntimeException('HTTP status code does not match'));
}
curl_close($ch);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Testcontainer\Wait;
use Symfony\Component\Process\Process;
use Testcontainer\Exception\ContainerNotReadyException;
class WaitForLog implements WaitInterface
{
public function __construct(private string $message, private bool $enableRegex = false)
{
}
public function wait(string $id): void
{
$process = new Process(['docker', 'logs', $id]);
$process->mustRun();
$output = $process->getOutput() . PHP_EOL . $process->getErrorOutput();
if ($this->enableRegex) {
if (!preg_match($this->message, $output)) {
throw new ContainerNotReadyException($id, new \RuntimeException('Message not found in logs'));
}
} else {
if (!str_contains($output, $this->message)) {
throw new ContainerNotReadyException($id, new \RuntimeException('Message not found in logs'));
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Testcontainer\Wait;
class WaitForNothing implements WaitInterface
{
public function wait(string $id): void
{
// does nothing
}
}
+10
View File
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Testcontainer\Wait;
interface WaitInterface
{
public function wait(string $id): void;
}