Skip to content

Commit

Permalink
feat: add command for creating S3 bucket
Browse files Browse the repository at this point in the history
  • Loading branch information
bensherred committed Dec 11, 2023
1 parent 37698aa commit fbffb94
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 2 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"illuminate/support": "^10.0"
},
"require-dev": {
"aws/aws-sdk-php": "^3.293",
"laravel/pint": "^1.10",
"orchestra/testbench": "^8.0",
"pestphp/pest": "^2.6",
Expand Down
24 changes: 24 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
services:
minio:
image: 'minio/minio:latest'
ports:
- '9000:9000'
- '8900:8900'
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: password
volumes:
- 'minio:/data/minio'
command: 'minio server /data/minio --console-address ":8900"'
healthcheck:
test:
- CMD
- curl
- '-f'
- 'http://localhost:9000/minio/health/live'
retries: 3
timeout: 5s

volumes:
minio:
driver: local
80 changes: 80 additions & 0 deletions src/Commands/StorageCreateBucketCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace RedExplosion\DevTools\Commands;

use Aws\S3\S3Client;
use Illuminate\Console\Command;

class StorageCreateBucketCommand extends Command
{
protected $signature = 'storage:create-bucket';

protected $description = 'Create an S3 bucket';

protected S3Client $client;

public function handle(): int
{
/** @var string|null $bucketName */
$bucketName = config(key: 'filesystems.disks.s3.bucket');

if (!$bucketName) {
$this->components->error(string: 'You must set the S3 bucket name before running this command!');

return self::FAILURE;
}

$this->client = $this->client();

if ($this->bucketExists(name: $bucketName)) {
$this->components->error(string: "S3 bucket [{$bucketName}] already exists!");

return self::FAILURE;
}

$this->createBucket(name: $bucketName);

$this->components->info(string: "S3 bucket [{$bucketName}] created successfully.");

return self::SUCCESS;
}

protected function client(): S3Client
{
return new S3Client([
'credentials' => [
'key' => config(key: 'filesystems.disks.s3.key'),
'secret' => config(key: 'filesystems.disks.s3.secret'),
],
'region' => config(key: 'filesystems.disks.s3.region'),
'version' => 'latest',
'endpoint' => config(key: 'filesystems.disks.s3.endpoint'),
'use_path_style_endpoint' => config(key: 'filesystems.disks.s3.use_path_style_endpoint'),
]);
}

protected function bucketExists(string $name): bool
{
$result = $this->client->listBuckets();

/** @var array $buckets */
$buckets = $result['Buckets'];

return array_search(needle: $name, haystack: array_column(array: $buckets, column_key: 'Name')) !== false;
}

protected function createBucket(string $name): void
{
$this->client->createBucket([
'Bucket' => $name,
'ACL' => 'public-read',
]);

$this->client->putBucketPolicy([
'Bucket' => $name,
'Policy' => '{"Version":"2012-10-17","Statement":[{"Sid":"AddPerm","Effect":"Allow","Principal":"*","Action":["s3:GetObject"],"Resource":["arn:aws:s3:::' . $name . '/*"]}]}',
]);
}
}
11 changes: 9 additions & 2 deletions src/DevToolsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@

class DevToolsServiceProvider extends ServiceProvider
{
public function register(): void {}
public function boot(): void
{
if (!$this->app->runningInConsole()) {
return;
}

public function boot(): void {}
$this->commands([
Commands\StorageCreateBucketCommand::class,
]);
}
}
53 changes: 53 additions & 0 deletions tests/StorageCreateBucketCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

afterEach(function (): void {
/** @var string|null $bucket */
$bucket = config(key: 'filesystems.disks.s3.bucket');

if (!$bucket) {
return;
}

$this->s3Client()->deleteBucket(['Bucket' => $bucket]);
});

it(description: 'creates an S3 bucket', closure: function (): void {
expect($this->s3Client()->listBuckets()['Buckets'])
->toBeEmpty();

$this->artisan(command: 'storage:create-bucket')
->expectsOutputToContain(string: 'S3 bucket [devtools] created successfully.')
->assertSuccessful();

expect($this->s3Client()->listBuckets()['Buckets'])
->toHaveCount(count: 1);
});

it(description: 'shows an error if the bucket already exists', closure: function (): void {
expect($this->s3Client()->listBuckets()['Buckets'])
->toBeEmpty();

$this->artisan(command: 'storage:create-bucket')
->expectsOutputToContain(string: 'S3 bucket [devtools] created successfully.')
->assertSuccessful();

expect($this->s3Client()->listBuckets()['Buckets'])
->toHaveCount(count: 1);

$this->artisan(command: 'storage:create-bucket')
->expectsOutputToContain(string: 'S3 bucket [devtools] already exists!')
->assertFailed();
});

it(description: 'shows an error if the bucket name is not set', closure: function (): void {
config()->set(key: 'filesystems.disks.s3.bucket', value: null);

$this->artisan(command: 'storage:create-bucket')
->expectsOutputToContain(string: 'You must set the S3 bucket name before running this command!')
->assertFailed();

expect($this->s3Client()->listBuckets()['Buckets'])
->toBeEmpty();
});
28 changes: 28 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace RedExplosion\DevTools\Tests;

use Aws\S3\S3Client;
use Illuminate\Config\Repository;
use Orchestra\Testbench\TestCase as Orchestra;
use RedExplosion\DevTools\DevToolsServiceProvider;

Expand All @@ -15,4 +17,30 @@ protected function getPackageProviders($app): array
DevtoolsServiceProvider::class,
];
}

protected function defineEnvironment($app): void
{
tap($app['config'], function (Repository $config): void {
$config->set(key: 'filesystems.disks.s3.key', value: 'minio');
$config->set(key: 'filesystems.disks.s3.secret', value: 'password');
$config->set(key: 'filesystems.disks.s3.region', value: 'us-east-1');
$config->set(key: 'filesystems.disks.s3.bucket', value: 'devtools');
$config->set(key: 'filesystems.disks.s3.endpoint', value: 'http://localhost:9000');
$config->set(key: 'filesystems.disks.s3.use_path_style_endpoint', value: true);
});
}

public function s3Client(): S3Client
{
return new S3Client([
'credentials' => [
'key' => config(key: 'filesystems.disks.s3.key'),
'secret' => config(key: 'filesystems.disks.s3.secret'),
],
'region' => config(key: 'filesystems.disks.s3.region'),
'version' => 'latest',
'endpoint' => config(key: 'filesystems.disks.s3.endpoint'),
'use_path_style_endpoint' => config(key: 'filesystems.disks.s3.use_path_style_endpoint'),
]);
}
}

0 comments on commit fbffb94

Please sign in to comment.