Skip to content

Commit

Permalink
Merge branch 'main' into fix/tm-mssql-arm
Browse files Browse the repository at this point in the history
  • Loading branch information
tonysm committed Jan 3, 2023
2 parents ff966ca + 531de0a commit 43eb799
Show file tree
Hide file tree
Showing 14 changed files with 305 additions and 31 deletions.
11 changes: 2 additions & 9 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,15 @@ jobs:
- name: Checkout code
uses: actions/checkout@v1

# - name: Cache dependencies
# uses: actions/cache@v1
# with:
# path: ~/.composer/cache/files
# key: dependencies-os-${{ matrix.os }}-php-${{ matrix.php }}-laravel-${{ matrix.laravel-version }}-composer-${{ hashFiles('**/composer.lock') }}
# restore-keys: dependencies-os-${{ matrix.os }}-php-${{ matrix.php }}-laravel-${{ matrix.laravel-version }}

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: posix, dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
extensions: posix, dom, curl, libxml, fileinfo, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
coverage: none

- name: Install dependencies
run: composer update --prefer-stable --prefer-dist --no-interaction --ignore-platform-req=ext-fileinfo
run: composer update --prefer-stable --prefer-dist --no-interaction --ignore-platform-req=ext-pcntl --ignore-platform-req=ext-posix

- name: Run tests
run: vendor/bin/phpunit
Expand Down
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,40 @@ takeout enable mysql --default
takeout enable redis meilisearch --default
```

#### Passthrough Container Arguments

You may specify extra arguments to the container after a `--` sepatator:

```bash
takeout enable mysql -- -hsome.mysql.host -usome-user
```

Notice that these are arguments for the container Entrypoint, not extra docker run options (see below).

#### Extra `docker run` Options

Under the hood, the `takeout enable` command generates a `docker run` command. Sometimes you may want to specify extra options to the `docker run` command such as an extra environment variable or an extra volume mapping. You can pass a string with all the extra `docker run` options using the `--run=` option:

```bash
takeout enable mysql --run="{docker-run-options}"
```

Which would generate the following command:

```bash
docker run {docker-run-options} {service-options} mysql/mysql-server
```

Where `{docker-run-options}` are the options you specify inside the `--run` option and `{service-options}` are generated based on the default options for that service.

#### Mixing `docker run` Options With Container Arguments

You may mix and match the `run` options with the container arguments:

```bash
takeout enable mysql --run="{docker-run-options}" -- -hsome.mysql.host -usome-user
```

### Disable a service

Show a list of all enabled services you can disable.
Expand Down Expand Up @@ -206,8 +240,6 @@ If you're working with us and are assigned to push a release, here's the easiest
4. Run the build once to make sure it works (`php ./builds/takeout list`)
5. Commit your build and push it up
6. [Draft a new release](https://github.com/tighten/takeout/releases/new) with both the tag version and release title of your tag (e.g. `v1.5.1`)
7. Set the body to be a bullet-point list with simple descriptions for each of the PRs merged, as well as the PR link in parentheses at the end. For example:

`- Fix internal Memcached port (#92)`
7. Use the "Generate release notes" button to generate release notes from the merged PRs.
8. Hit `Publish release`
9. Profit
9. Profit 😆
18 changes: 14 additions & 4 deletions app/Commands/EnableCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class EnableCommand extends Command

const MENU_TITLE = 'Takeout containers to enable';

protected $signature = 'enable {serviceNames?*} {--default}';
protected $signature = 'enable {serviceNames?*} {--default} {--run= : Pass any extra docker run options.}';
protected $description = 'Enable services.';
protected $environment;
protected $services;
Expand All @@ -31,10 +31,16 @@ public function handle(Environment $environment, Services $services): void
$passthroughOptions = $this->extractPassthroughOptions($this->serverArguments());

$useDefaults = $this->option('default');
$runOptions = $this->option('run');

if (filled($services)) {
if ($runOptions && is_array($services) && count($services) > 1) {
$this->error('The --run options should only be used for enabling a single service.');
return;
}

foreach ($services as $service) {
$this->enable($service, $useDefaults, $passthroughOptions);
$this->enable($service, $useDefaults, $passthroughOptions, $runOptions);
}

return;
Expand Down Expand Up @@ -63,6 +69,10 @@ public function serverArguments(): array
$string[] = '--default';
}

if ($this->option('run')) {
$string[] = '--run';
}

return $string;
}

Expand Down Expand Up @@ -205,9 +215,9 @@ public function enableableServicesByCategory(): array
->toArray();
}

public function enable(string $service, bool $useDefaults = false, array $passthroughOptions = []): void
public function enable(string $service, bool $useDefaults = false, array $passthroughOptions = [], string $runOptions = null): void
{
$fqcn = $this->services->get($service);
app($fqcn)->enable($useDefaults, $passthroughOptions);
app($fqcn)->enable($useDefaults, $passthroughOptions, $runOptions);
}
}
12 changes: 8 additions & 4 deletions app/Services/BaseService.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public static function name(): string
return static::$displayName ?? Str::afterLast(static::class, '\\');
}

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

Expand All @@ -79,8 +79,12 @@ public function enable(bool $useDefaults = false, array $passthroughOptions = []

try {
$this->docker->bootContainer(
$this->sanitizeDockerRunTemplate($this->dockerRunTemplate) . $this->buildPassthroughOptionsString($passthroughOptions),
$this->buildParameters()
join(' ', array_filter([
$runOptions,
$this->sanitizeDockerRunTemplate($this->dockerRunTemplate),
$this->buildPassthroughOptionsString($passthroughOptions),
])),
$this->buildParameters(),
);

$this->info("\nService enabled!");
Expand Down Expand Up @@ -232,6 +236,6 @@ public function buildPassthroughOptionsString(array $passthroughOptions): string
return '';
}

return ' ' . join(' ', $passthroughOptions);
return join(' ', $passthroughOptions);
}
}
40 changes: 40 additions & 0 deletions app/Services/Couchbase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Services;

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

protected $imageName = 'couchbase';
protected $defaultPrompts = [
[
'shortname' => 'ports',
'prompt' => 'Which port(s) would you like %s to use? (Must be 6 ports)',
'default' => '8091-8096',
],
[
'shortname' => 'tag',
'prompt' => 'Which tag (version) of %s would you like to use?',
'default' => 'latest',
],
[
'shortname' => 'encrypted_ports',
'prompt' => 'Which encrypted port(s) would you like %s to use? (Must be 2 ports)',
'default' => '11210-11211',
],
[
'shortname' => 'volume',
'prompt' => 'What is the Docker volume name?',
'default' => 'couchbase_data',
],
];

protected $dockerRunTemplate = '-d \
-p "${:ports}":8091-8096 \
-p "${:encrypted_ports}":11210-11211 \
-v "${:volume}":/opt/couchbase/var \
"${:image_name}":"${:tag}"';

protected static $displayName = 'Couchbase';
}
8 changes: 7 additions & 1 deletion app/Services/ElasticSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ class ElasticSearch extends BaseService
'prompt' => 'What is the Docker volume name?',
'default' => 'elastic_data',
],
[
'shortname' => 'enable_security',
'prompt' => 'Enable security (true or false)?',
'default' => 'true',
],
];

protected $dockerRunTemplate = '-p "${:port}":9200 \
-e "discovery.type=single-node" \
-e "discovery.type=single-node" \
-e xpack.security.enabled="${:enable_security}" \
-v "${:volume}":/usr/share/elasticsearch/data \
"${:organization}"/"${:image_name}":"${:tag}"';

Expand Down
6 changes: 6 additions & 0 deletions app/Services/OpenSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@ class OpenSearch extends BaseService
'prompt' => 'Which host port would you like to be used by the performance analyzer?',
'default' => 9600,
],
[
'shortname' => 'disable_security',
'prompt' => 'Disable security plugin (true or false)?',
'default' => 'false',
],
];

protected $dockerRunTemplate = '-p "${:port}":9200 \
-p "${:analyzer_port}":9600 \
-e DISABLE_SECURITY_PLUGIN="${:disable_security}" \
-e "discovery.type=single-node" \
-v "${:volume}":/usr/share/opensearch/data \
"${:organization}"/"${:image_name}":"${:tag}"';
Expand Down
Binary file modified builds/takeout
Binary file not shown.
14 changes: 12 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"require": {
"php": "^8.0",
"ext-json": "*",
"ext-pcntl": "*",
"ext-posix": "*",
"guzzlehttp/psr7": "^1.7"
},
"require-dev": {
Expand Down Expand Up @@ -48,10 +50,18 @@
"optimize-autoloader": true,
"platform-check": false,
"platform": {
"ext-pcntl": "7.2",
"ext-posix": "7.2"
"php": "8.0.2"
}
},
"scripts": {
"lint:check": [
"tlint",
"phpcs"
],
"lint:fix": [
"phpcbf"
]
},
"minimum-stability": "dev",
"prefer-stable": true,
"bin": ["builds/takeout"]
Expand Down
105 changes: 102 additions & 3 deletions tests/Feature/BaseServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@
use LaravelZero\Framework\Commands\Command;
use Mockery as M;
use Symfony\Component\Process\Process;
use Tests\Support\FakeService;
use Tests\TestCase;

class BaseServiceTest extends TestCase
{
/** @test */
function it_generates_shortname()
public function it_generates_shortname()
{
$meilisearch = app(MeiliSearch::class);
$this->assertEquals('meilisearch', $meilisearch->shortName());
}

/** @test */
function it_enables_services()
public function it_enables_services()
{
$service = app(MeiliSearch::class);

Expand Down Expand Up @@ -66,7 +67,7 @@ function it_enables_services()
}

/** @test */
function it_can_receive_a_custom_image_in_the_tag()
public function it_can_receive_a_custom_image_in_the_tag()
{
$service = app(PostgreSql::class);

Expand Down Expand Up @@ -110,4 +111,102 @@ function it_can_receive_a_custom_image_in_the_tag()
$service = app(PostgreSql::class); // Extends BaseService
$service->enable();
}

/** @test */
public function it_can_receive_container_arguments_via_passthrough()
{
$service = app(FakeService::class);
$passthroughOptions = ['-h=127.0.0.1', '-usome-user'];

app()->instance('console', M::mock(Command::class, function ($mock) use ($service) {
$defaultPort = $service->defaultPort();
$mock->shouldReceive('ask')->with('Which host port would you like _test_image to use?', $defaultPort)->andReturn(12345);
$mock->shouldReceive('ask')->with('Which tag (version) of _test_image would you like to use?', 'latest')->andReturn('latest');
$mock->shouldIgnoreMissing();
}));

$this->mock(Shell::class, function ($mock) {
$process = M::mock(Process::class);
$process->shouldReceive('isSuccessful')->andReturn(false);
$process->shouldReceive('getOutput')->andReturn('');

$mock->shouldReceive('execQuietly')->andReturn($process);
});

$this->mock(Docker::class, function ($mock) use ($service, $passthroughOptions) {
$mock->shouldReceive('isInstalled')->andReturn(true);
$mock->shouldReceive('imageIsDownloaded')->andReturn(true);
$mock->shouldReceive('volumeIsAvailable')->andReturn(true);

// This is the actual assertion
$mock->shouldReceive('bootContainer')->with(
join(' ', [
$service->sanitizeDockerRunTemplate($service->dockerRunTemplate()),
$service->buildPassthroughOptionsString($passthroughOptions),
]),
[
'organization' => 'tighten',
'image_name' => '_test_image',
'port' => 12345,
'tag' => 'latest',
'container_name' => 'TO--fakeservice--latest--12345',
'alias' => 'fakeservice-latest',
]
)->once();
});

// We need to create a new instance of the service so it can use the mocked objects...
$service = app(FakeService::class);
$service->enable(passthroughOptions: $passthroughOptions);
}

/** @test */
public function it_accepts_run_options_and_passthrough_options()
{
$service = app(FakeService::class);
$passthroughOptions = ['-h=127.0.0.1', '-usome-user'];
$runOptions = '--restart unless-stopped -e "LOREM=IPSUM"';

app()->instance('console', M::mock(Command::class, function ($mock) use ($service) {
$defaultPort = $service->defaultPort();
$mock->shouldReceive('ask')->with('Which host port would you like _test_image to use?', $defaultPort)->andReturn(12345);
$mock->shouldReceive('ask')->with('Which tag (version) of _test_image would you like to use?', 'latest')->andReturn('latest');
$mock->shouldIgnoreMissing();
}));

$this->mock(Shell::class, function ($mock) {
$process = M::mock(Process::class);
$process->shouldReceive('isSuccessful')->andReturn(false);
$process->shouldReceive('getOutput')->andReturn('');

$mock->shouldReceive('execQuietly')->andReturn($process);
});

$this->mock(Docker::class, function ($mock) use ($service, $runOptions, $passthroughOptions) {
$mock->shouldReceive('isInstalled')->andReturn(true);
$mock->shouldReceive('imageIsDownloaded')->andReturn(true);
$mock->shouldReceive('volumeIsAvailable')->andReturn(true);

// This is the actual assertion
$mock->shouldReceive('bootContainer')->with(
join(' ', [
$runOptions,
$service->sanitizeDockerRunTemplate($service->dockerRunTemplate()),
$service->buildPassthroughOptionsString($passthroughOptions),
]),
[
'organization' => 'tighten',
'image_name' => '_test_image',
'port' => 12345,
'tag' => 'latest',
'container_name' => 'TO--fakeservice--latest--12345',
'alias' => 'fakeservice-latest',
]
)->once();
});

// We need to create a new instance of the service so it can use the mocked objects...
$service = app(FakeService::class);
$service->enable(passthroughOptions: $passthroughOptions, runOptions: $runOptions);
}
}
Loading

0 comments on commit 43eb799

Please sign in to comment.