Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Providers;

use App\Shell\Platform;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
Expand All @@ -23,6 +24,6 @@ public function boot()
*/
public function register()
{
//
$this->app->scoped(Platform::class);
}
}
62 changes: 31 additions & 31 deletions app/Services/BaseService.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ public static function name(): string
return static::$displayName ?? Str::afterLast(static::class, '\\');
}

public function enable(bool $useDefaults = false, array $passthroughOptions = [], string $runOptions = null): void
public static function category(): string
{
return static::$category ?? 'Other';
}

public function enable(bool $useDefaults = false, array $passthroughOptions = [], ?string $runOptions = null): void
{
$this->useDefaults = $useDefaults;

Expand All @@ -80,9 +85,9 @@ public function enable(bool $useDefaults = false, array $passthroughOptions = []

try {
$this->docker->bootContainer(
join(' ', array_filter([
implode(' ', array_filter([
$runOptions,
$this->sanitizeDockerRunTemplate($this->dockerRunTemplate),
$this->sanitizeDockerRunTemplate($this->dockerRunTemplate()),
$this->buildPassthroughOptionsString($passthroughOptions),
])),
$this->buildParameters(),
Expand Down Expand Up @@ -111,11 +116,6 @@ public function forwardShell(): void
$this->docker->forwardShell($service['container_id'], $this->shellCommand());
}

protected function shellCommand(): string
{
return 'bash';
}

public function organization(): string
{
return $this->organization;
Expand All @@ -126,11 +126,6 @@ public function imageName(): string
return $this->imageName;
}

public static function category(): string
{
return static::$category ?? 'Other';
}

public function shortName(): string
{
return strtolower(class_basename(static::class));
Expand All @@ -146,6 +141,29 @@ public function defaultPort(): int
return $this->defaultPort;
}

public function sanitizeDockerRunTemplate($dockerRunTemplate): string
{
if ($this->environment->isWindowsOs()) {
return stripslashes($dockerRunTemplate);
}

return $dockerRunTemplate;
}

public function buildPassthroughOptionsString(array $passthroughOptions): string
{
if (empty($passthroughOptions)) {
return '';
}

return implode(' ', $passthroughOptions);
}

protected function shellCommand(): string
{
return 'bash';
}

protected function ensureImageIsDownloaded(): void
{
if ($this->docker->imageIsDownloaded($this->organization, $this->imageName, $this->tag)) {
Expand Down Expand Up @@ -244,22 +262,4 @@ protected function containerName(): string

return 'TO--' . $this->shortName() . '--' . $this->tag . $portTag;
}

public function sanitizeDockerRunTemplate($dockerRunTemplate): string
{
if ($this->environment->isWindowsOs()) {
return stripslashes($dockerRunTemplate);
}

return $dockerRunTemplate;
}

public function buildPassthroughOptionsString(array $passthroughOptions): string
{
if (empty($passthroughOptions)) {
return '';
}

return join(' ', $passthroughOptions);
}
}
18 changes: 16 additions & 2 deletions app/Services/MsSql.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
namespace App\Services;

use App\Shell\MicrosoftDockerTags;
use App\Shell\Platform;

class MsSql extends BaseService
{
protected static $category = Category::DATABASE;

protected static $displayName = 'MS SQL Server';

protected $organization = 'mcr.microsoft.com';
protected $imageName = 'azure-sql-edge';
protected $imageName = 'mssql/server';
protected $dockerTagsClass = MicrosoftDockerTags::class;
protected $defaultPort = 1433;
protected $prompts = [
Expand All @@ -27,9 +30,20 @@ class MsSql extends BaseService

protected $dockerRunTemplate = '-p "${:port}":1433 \
-e ACCEPT_EULA=Y \
-e MSSQL_PID=Express \
-e SA_PASSWORD="${:sa_password}" \
-v "${:volume}":/var/opt/mssql \
"${:organization}"/"${:image_name}":"${:tag}"';

protected static $displayName = 'MS SQL Server';
public function dockerRunTemplate(): string
{
// The Microsoft image doesn't provide a proper ARM64 build,
// so we need to rely on Rosetta for Mac users. We can do
// that by specifying a platform such as `linux/amd64`.

return match (Platform::isArm()) {
true => '--platform linux/amd64 \\' . PHP_EOL . $this->dockerRunTemplate,
default => $this->dockerRunTemplate,
};
}
}
15 changes: 4 additions & 11 deletions app/Shell/DockerTags.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,9 @@ public function getTags(): Collection
{
$response = json_decode($this->getTagsResponse()->getContents(), true);

$platform = $this->platform();

return collect($response['results'])
->when(in_array($platform, $this->armArchitectures, true), $this->onlyArmImagesFilter())
->when(! in_array($platform, $this->armArchitectures, true), $this->onlyNonArmImagesFilter())
->when(Platform::isArm(), $this->onlyArmImagesFilter())
->when(! Platform::isArm(), $this->onlyNonArmImagesFilter())
->pluck('name')
->sort(new VersionComparator)
->values();
Expand All @@ -61,7 +59,7 @@ protected function onlyArmImagesFilter()
return $tags->filter(function ($tag) {
$supportedArchs = collect($tag['images'])->pluck('architecture');

foreach ($this->armArchitectures as $arch) {
foreach (Platform::$armArchitectures as $arch) {
if ($supportedArchs->contains($arch)) {
return true;
}
Expand All @@ -85,16 +83,11 @@ protected function onlyNonArmImagesFilter()
// still be other options in the supported architectures
// so we can consider that the tag is not arm-only.

return $supportedArchitectures->diff($this->armArchitectures)->count() > 0;
return $supportedArchitectures->diff(Platform::$armArchitectures)->count() > 0;
});
};
}

protected function platform(): string
{
return php_uname('m');
}

protected function getTagsResponse(): StreamInterface
{
return $this->guzzle
Expand Down
33 changes: 33 additions & 0 deletions app/Shell/Platform.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Shell;

class Platform
{
public static array $armArchitectures = ['arm64', 'aarch64'];

public function __construct(
private ?string $platform = null,
) {
}

public static function make(): static
{
return resolve(Platform::class);
}

public static function fake(string $platform): Platform
{
return app()->instance(Platform::class, new Platform($platform));
}

public static function isArm(): bool
{
return in_array(static::make()->platform(), static::$armArchitectures, true);
}

protected function platform(): string
{
return $this->platform ??= php_uname('m');
}
}
31 changes: 14 additions & 17 deletions tests/Feature/DockerTagsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,18 @@
use App\Services\MySql;
use App\Services\PostgreSql;
use App\Shell\DockerTags;
use App\Shell\Platform;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use Mockery as M;
use Tests\Support\FakePlatformDockerTags;
use Tests\TestCase;

class DockerTagsTest extends TestCase
{
public static function armPlatforms(): array
{
return [
[FakePlatformDockerTags::M1_ARM_PLATFORM],
[FakePlatformDockerTags::LINUX_ARM_PLATFORM],
];
}

/** @test */
function it_gets_the_latest_tag_not_named_latest()
public function it_gets_the_latest_tag_not_named_latest()
{
$dockerTags = M::mock(DockerTags::class, [app(Client::class), app(MySql::class)])->makePartial();
$dockerTags->shouldReceive('getTags')->andReturn(collect(['latest', 'some named tag', '1.0.0']));
Expand All @@ -33,7 +25,7 @@ function it_gets_the_latest_tag_not_named_latest()
}

/** @test */
function if_latest_is_the_only_tag_it_returns_latest()
public function if_latest_is_the_only_tag_it_returns_latest()
{
$dockerTags = M::mock(DockerTags::class, [app(Client::class), app(MySql::class)])->makePartial();
$dockerTags->shouldReceive('getTags')->andReturn(collect(['latest']));
Expand All @@ -42,7 +34,7 @@ function if_latest_is_the_only_tag_it_returns_latest()
}

/** @test */
function it_sorts_the_versions_naturally()
public function it_sorts_the_versions_naturally()
{
$postgres = app(PostgreSql::class);
$dockerTags = app(DockerTags::class, ['service' => $postgres]);
Expand All @@ -55,25 +47,30 @@ function it_sorts_the_versions_naturally()
/**
* @test
*
* @dataProvider armPlatforms
* @testWith ["arm64"]
* ["aarch64"]
*/
function it_detects_arm_based_images_when_running_on_arm64_based_host($platform)
public function it_detects_arm_based_images_when_running_on_arm64_based_host($platform)
{
$handlerStack = HandlerStack::create($this->mockImagesResponseHandler());
$client = new Client(['handler' => $handlerStack]);

$dockerTags = (new FakePlatformDockerTags($client, app(MySql::class)))->withFakePlatform($platform);
Platform::fake($platform);

$dockerTags = new DockerTags($client, app(MySql::class));

$this->assertEquals('1.0.0-arm64', $dockerTags->getLatestTag());
}

/** @test */
function it_gets_latest_tag_on_intel_platform()
public function it_gets_latest_tag_on_intel_platform()
{
$handlerStack = HandlerStack::create($this->mockImagesResponseHandler());
$client = new Client(['handler' => $handlerStack]);

$dockerTags = (new FakePlatformDockerTags($client, app(MySql::class)))->withFakePlatform(FakePlatformDockerTags::INTEL_ARM_PLATFORM);
Platform::fake('x86_64');

$dockerTags = new DockerTags($client, app(MySql::class));

$this->assertEquals('1.0.0', $dockerTags->getLatestTag());
}
Expand Down
26 changes: 0 additions & 26 deletions tests/Support/FakePlatformDockerTags.php

This file was deleted.