Skip to content

Commit

Permalink
Merge pull request #1 from DockerPhpClient/test/tar-archive
Browse files Browse the repository at this point in the history
add Image Manager
  • Loading branch information
pselge-daparto committed Nov 12, 2020
2 parents a3eb08d + fcb0e91 commit 551ed7d
Show file tree
Hide file tree
Showing 11 changed files with 252 additions and 57 deletions.
5 changes: 5 additions & 0 deletions README.md
Expand Up @@ -10,6 +10,11 @@
- delete
- wait
- logs
- list

### Images
- create
- delete

## Examples
See the examples folder for example code
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -18,7 +18,8 @@
"php-http/socket-client": "^2.0",
"php-http/client-common": "^2.3",
"guzzlehttp/psr7": "^1.7",
"docker-client/open-api": "6.1.40.*"
"docker-client/open-api": "6.1.40.*",
"splitbrain/php-archive": "*"
},
"prefer-stable": true,
"minimum-stability": "dev"
Expand Down
60 changes: 59 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions examples/containers-create-and-delete.php
Expand Up @@ -19,6 +19,11 @@
// create a named container
$client->containers()->create($container, ['name' => $containerName]);


// list all images
$containers = $client->containers()->list();
echo implode("", $client->containers()->toContainerListLog($containers));

// start container
$client->containers()->start($containerName);

Expand Down
12 changes: 11 additions & 1 deletion examples/images-create-and-delete.php
@@ -1,13 +1,23 @@
<?php

use Docker\Client\Context\ContextFactory;
use Docker\Client\Context\ImageContext;
use Docker\Client\Context\ImageContextFactory;
use Docker\Client\DockerClientFactory;
use splitbrain\PHPArchive\Tar;

require_once __DIR__ . '/../vendor/autoload.php';

$client = DockerClientFactory::create();

// build image
$client->images()->build(__DIR__ . '/app/', ['t' => 'new-test-image']);
$factory = new ContextFactory(Tar::class);
$context = $factory->createImageContext(__DIR__ . '/app/');
$buildInfos = $client->images()->build($context, ['t' => 'new-test-image']);

// print build log and receive id
echo implode("", $client->images()->toBuildLog($buildInfos));
echo "\nImage ID: " . $client->images()->toBuildId($buildInfos) . "\n";

// delete image
$client->images()->delete('new-test-image');
Expand Down
25 changes: 25 additions & 0 deletions src/Client/Context/ContextFactory.php
@@ -0,0 +1,25 @@
<?php /** @noinspection PhpMissingFieldTypeInspection */

namespace Docker\Client\Context;

class ContextFactory
{
/**
* @var class-string<Archive> $archiveClass
*/
private $archiveClass;

/**
* ImageContextFactory constructor.
* @param class-string<Archive> $archiveClass
*/
public function __construct($archiveClass)
{
$this->archiveClass = $archiveClass;
}

public function createImageContext(string $contextDirectory): ImageContext
{
return new ImageContext($contextDirectory, new $this->archiveClass());
}
}
55 changes: 30 additions & 25 deletions src/Client/Context/ImageContext.php
Expand Up @@ -3,43 +3,48 @@
namespace Docker\Client\Context;

use Docker\Client\Stream\TarStream;
use FilesystemIterator;
use GuzzleHttp\Psr7\Utils;
use Psr\Http\Message\StreamInterface;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use splitbrain\PHPArchive\Archive;
use splitbrain\PHPArchive\ArchiveCorruptedException;
use splitbrain\PHPArchive\ArchiveIllegalCompressionException;
use splitbrain\PHPArchive\ArchiveIOException;
use splitbrain\PHPArchive\FileInfoException;
use splitbrain\PHPArchive\Tar;

class ImageContext
{
private string $directory;
/**
* @var resource $process
*/
private $process;
protected const FLAGS =
FilesystemIterator::KEY_AS_PATHNAME |
FilesystemIterator::CURRENT_AS_FILEINFO |
FilesystemIterator::SKIP_DOTS;

/**
* @var resource $stream
*/
private $stream;
private string $contextDirectory;
private Archive $archive;

public function __construct(string $directory)
public function __construct($contextDirectory, Archive $archive)
{
$this->directory = $directory;
$this->contextDirectory = $contextDirectory;
$this->archive = $archive;
}

public function getStream(): StreamInterface
/**
* @return StreamInterface
* @throws ArchiveIOException
*/
public function toStream(): StreamInterface
{
$this->process = proc_open("/usr/bin/env tar c .", [["pipe", "r"], ["pipe", "w"], ["pipe", "w"]], $pipes, $this->directory);
$this->stream = $pipes[1];
$this->archive->create();

return new TarStream($this->stream);
}

public function __destruct()
{
if (is_resource($this->process)) {
proc_close($this->process);
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->contextDirectory, static::FLAGS)) as $file) {
if ($file->isFile()) {
$this->archive->addFile($file->getPathname(), str_replace($this->contextDirectory, '', $file->getPathname()));
}
}

if (is_resource($this->stream)) {
fclose($this->stream);
}
return Utils::streamFor($this->archive->getArchive());
}

}
40 changes: 40 additions & 0 deletions src/Client/Endpoint/ImageBuild.php
@@ -0,0 +1,40 @@
<?php


namespace Docker\Client\Endpoint;

use Docker\OpenAPI\Endpoint\ImageBuild as BaseEndpoint;
use Docker\OpenAPI\Model\BuildInfo;

class ImageBuild extends BaseEndpoint
{
/**
* {@inheritDoc}
*/
protected function transformResponseBody(string $body, int $status, \Symfony\Component\Serializer\SerializerInterface $serializer, ?string $contentType)
{
if (200 === $status) {
$data = $this->logToJson($body);
return $serializer->deserialize($data, BuildInfo::class . '[]', 'json');
}
return parent::transformResponseBody($body, $status, $serializer, $contentType);
}

/**
* @param string $body
* @return string
*/
protected function logToJson(string $body): string
{
$data = explode("\n", $body);

// remove empty line(s)
$data = array_filter($data, static function ($line) {
return null !== $line && !empty($line);
});

// rebuild as json array
$data = "[" . implode(",", $data) . "]";
return $data;
}
}
22 changes: 22 additions & 0 deletions src/Client/Manager/ContainerManager.php
Expand Up @@ -6,6 +6,7 @@

use Docker\OpenAPI\Client;
use Docker\OpenAPI\Model\ContainersCreatePostBody;
use Docker\OpenAPI\Model\ContainerSummaryItem;
use Psr\Http\Message\ResponseInterface;

class ContainerManager
Expand Down Expand Up @@ -44,4 +45,25 @@ public function logs(string $idOrName): string
$response = $this->apiClient->containerAttach($idOrName, ['logs' => true, 'stdout' => true, 'stderr' => true], Client::FETCH_RESPONSE);
return ($response instanceof ResponseInterface) ? $response->getBody()->getContents() : "";
}

/**
* @param array $queryParameters
* @return ContainerSummaryItem[]
*/
public function list(array $queryParameters = []): array
{
return $this->apiClient->containerList($queryParameters, $this->fetchType);
}

/**
* TODO: WIP
* @param ContainerSummaryItem[] $containers
* @return string[]
*/
public function toContainerListLog(array $containers): array
{
return array_map(static function (ContainerSummaryItem $container) {
return substr($container->getId(), 8) . " " . $container->getNames()[0] . " " . $container->getImage() . " (" . substr($container->getImageID(), 8) . ")\n";
}, $containers);
}
}

0 comments on commit 551ed7d

Please sign in to comment.