-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from dinamic/feature/wait-for-port
Support for waiting on tcp port to open
- Loading branch information
Showing
5 changed files
with
214 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Testcontainer\Trait; | ||
|
||
use JsonException; | ||
use Symfony\Component\Process\Process; | ||
use Testcontainer\Container\Container; | ||
use UnexpectedValueException; | ||
|
||
/** | ||
* @phpstan-import-type ContainerInspect from Container | ||
* @phpstan-import-type DockerNetwork from Container | ||
*/ | ||
trait DockerContainerAwareTrait | ||
{ | ||
/** | ||
* @param string $containerId | ||
* @param string|null $networkName | ||
* @param ContainerInspect|null $inspectedData | ||
* @return string | ||
* | ||
* @throws JsonException | ||
*/ | ||
private static function dockerContainerAddress(string $containerId, ?string $networkName = null, ?array $inspectedData = null): string | ||
{ | ||
if (! is_array($inspectedData)) { | ||
$inspectedData = self::dockerContainerInspect($containerId); | ||
} | ||
|
||
if (is_string($networkName)) { | ||
$containerAddress = $inspectedData[0]['NetworkSettings']['Networks'][$networkName]['IPAddress'] ?? null; | ||
|
||
if (is_string($containerAddress)) { | ||
return $containerAddress; | ||
} | ||
} | ||
|
||
$containerAddress = $inspectedData[0]['NetworkSettings']['IPAddress'] ?? null; | ||
|
||
if (is_string($containerAddress)) { | ||
return $containerAddress; | ||
} | ||
|
||
throw new UnexpectedValueException('Unable to find container IP address'); | ||
} | ||
|
||
/** | ||
* @param string $containerId | ||
* @return ContainerInspect | ||
* | ||
* @throws JsonException | ||
*/ | ||
private static function dockerContainerInspect(string $containerId): array | ||
{ | ||
$process = new Process(['docker', 'inspect', $containerId]); | ||
$process->mustRun(); | ||
|
||
/** @var ContainerInspect */ | ||
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Testcontainer\Wait; | ||
|
||
use JsonException; | ||
use RuntimeException; | ||
use Testcontainer\Exception\ContainerNotReadyException; | ||
use Testcontainer\Trait\DockerContainerAwareTrait; | ||
|
||
final class WaitForTcpPortOpen implements WaitInterface | ||
{ | ||
use DockerContainerAwareTrait; | ||
|
||
public function __construct(private readonly int $port, private readonly ?string $network = null) | ||
{ | ||
} | ||
|
||
public static function make(int $port, ?string $network = null): self | ||
{ | ||
return new self($port, $network); | ||
} | ||
|
||
/** | ||
* @throws JsonException | ||
*/ | ||
public function wait(string $id): void | ||
{ | ||
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')); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters