Skip to content

Commit d62a737

Browse files
authored
Merge pull request #377 from origamiphp/feature/database-details
Add a command to check the database configuration
2 parents f178427 + 470269d commit d62a737

19 files changed

+558
-625
lines changed

psalm.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<psalm
3-
totallyTyped="true"
4-
errorLevel="3"
53
allowStringToStandInForClass="true"
4+
cacheDirectory="var/psalm"
5+
errorBaseline="psalm_baseline.xml"
6+
errorLevel="3"
7+
totallyTyped="true"
68
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
79
xmlns="https://getpsalm.org/schema/config"
810
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
9-
errorBaseline="psalm_baseline.xml"
1011
>
1112
<projectFiles>
1213
<directory name="src"/>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Command\Database;
6+
7+
use App\Command\AbstractBaseCommand;
8+
use App\Exception\OrigamiExceptionInterface;
9+
use App\Service\ApplicationContext;
10+
use App\Service\Middleware\Database;
11+
use App\Service\Wrapper\OrigamiStyle;
12+
use Symfony\Component\Console\Command\Command;
13+
use Symfony\Component\Console\Input\InputInterface;
14+
use Symfony\Component\Console\Output\OutputInterface;
15+
16+
class DetailsCommand extends AbstractBaseCommand
17+
{
18+
/** {@inheritdoc} */
19+
protected static $defaultName = 'origami:database:details';
20+
/** {@inheritdoc} */
21+
protected static $defaultDescription = 'Shows the database details of the running environment';
22+
23+
public function __construct(
24+
private ApplicationContext $applicationContext,
25+
private Database $database,
26+
string $name = null
27+
) {
28+
parent::__construct($name);
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
protected function execute(InputInterface $input, OutputInterface $output): int
35+
{
36+
$io = new OrigamiStyle($input, $output);
37+
38+
try {
39+
$this->applicationContext->loadEnvironment($input);
40+
41+
$io->listing([
42+
"Type: {$this->database->getDatabaseType()}",
43+
"Version: {$this->database->getDatabaseVersion()}",
44+
"Username: {$this->database->getDatabaseUsername()}",
45+
"Password: {$this->database->getDatabasePassword()}",
46+
]);
47+
} catch (OrigamiExceptionInterface $exception) {
48+
$io->error($exception->getMessage());
49+
50+
return Command::FAILURE;
51+
}
52+
53+
return Command::SUCCESS;
54+
}
55+
}

src/Command/Database/DumpCommand.php

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
namespace App\Command\Database;
66

77
use App\Command\AbstractBaseCommand;
8+
use App\Exception\DatabaseException;
9+
use App\Exception\FilesystemException;
10+
use App\Exception\InvalidConfigurationException;
811
use App\Exception\OrigamiExceptionInterface;
912
use App\Service\ApplicationContext;
13+
use App\Service\Middleware\Binary\Docker;
1014
use App\Service\Middleware\Database;
1115
use App\Service\Wrapper\OrigamiStyle;
1216
use Symfony\Component\Console\Command\Command;
@@ -24,6 +28,7 @@ class DumpCommand extends AbstractBaseCommand
2428
public function __construct(
2529
private ApplicationContext $applicationContext,
2630
private Database $database,
31+
private Docker $docker,
2732
string $name = null
2833
) {
2934
parent::__construct($name);
@@ -58,7 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
5863
}
5964

6065
$path = ltrim($input->getArgument('path'), '/');
61-
$this->database->dump($environment->getLocation().'/'.$path);
66+
$this->dump($environment->getLocation().'/'.$path);
6267

6368
$io->success('Database dump successfully executed.');
6469
} catch (OrigamiExceptionInterface $exception) {
@@ -69,4 +74,38 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6974

7075
return Command::SUCCESS;
7176
}
77+
78+
/**
79+
* Triggers the database dump process according to the database type.
80+
*
81+
* @throws DatabaseException
82+
* @throws InvalidConfigurationException
83+
* @throws FilesystemException
84+
*/
85+
private function dump(string $path): void
86+
{
87+
switch ($this->database->getDatabaseType()) {
88+
case 'mariadb':
89+
case 'mysql':
90+
$username = $this->database->getDatabaseUsername();
91+
$password = $this->database->getDatabasePassword();
92+
93+
if (!$this->docker->dumpMysqlDatabase($username, $password, $path)) {
94+
throw new DatabaseException('Unable to complete the MySQL dump process.');
95+
}
96+
break;
97+
98+
case 'postgres':
99+
$username = $this->database->getDatabaseUsername();
100+
$password = $this->database->getDatabasePassword();
101+
102+
if (!$this->docker->dumpPostgresDatabase($username, $password, $path)) {
103+
throw new DatabaseException('Unable to complete the Postgres dump process.');
104+
}
105+
break;
106+
107+
default:
108+
throw new DatabaseException('The database type in use is not yet supported.');
109+
}
110+
}
72111
}

src/Command/Database/RestoreCommand.php

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
namespace App\Command\Database;
66

77
use App\Command\AbstractBaseCommand;
8+
use App\Exception\DatabaseException;
9+
use App\Exception\FilesystemException;
10+
use App\Exception\InvalidConfigurationException;
811
use App\Exception\OrigamiExceptionInterface;
912
use App\Service\ApplicationContext;
13+
use App\Service\Middleware\Binary\Docker;
1014
use App\Service\Middleware\Database;
1115
use App\Service\Wrapper\OrigamiStyle;
1216
use Symfony\Component\Console\Command\Command;
@@ -24,6 +28,7 @@ class RestoreCommand extends AbstractBaseCommand
2428
public function __construct(
2529
private ApplicationContext $applicationContext,
2630
private Database $database,
31+
private Docker $docker,
2732
string $name = null
2833
) {
2934
parent::__construct($name);
@@ -58,7 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
5863
}
5964

6065
$path = ltrim($input->getArgument('path'), '/');
61-
$this->database->restore($environment->getLocation().'/'.$path);
66+
$this->restore($environment->getLocation().'/'.$path);
6267

6368
$io->success('Database restore successfully executes.');
6469
} catch (OrigamiExceptionInterface $exception) {
@@ -69,4 +74,42 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6974

7075
return Command::SUCCESS;
7176
}
77+
78+
/**
79+
* Triggers the database restore process according to the database type.
80+
*
81+
* @throws DatabaseException
82+
* @throws InvalidConfigurationException
83+
* @throws FilesystemException
84+
*/
85+
private function restore(string $path): void
86+
{
87+
if (!is_file($path)) {
88+
throw new DatabaseException('Unable to find the backup file to restore.');
89+
}
90+
91+
switch ($this->database->getDatabaseType()) {
92+
case 'mariadb':
93+
case 'mysql':
94+
$username = $this->database->getDatabaseUsername();
95+
$password = $this->database->getDatabasePassword();
96+
97+
if (!$this->docker->restoreMysqlDatabase($username, $password, $path)) {
98+
throw new DatabaseException('Unable to complete the MySQL restore process.');
99+
}
100+
break;
101+
102+
case 'postgres':
103+
$username = $this->database->getDatabaseUsername();
104+
$password = $this->database->getDatabasePassword();
105+
106+
if (!$this->docker->restorePostgresDatabase($username, $password, $path)) {
107+
throw new DatabaseException('Unable to complete the Postgres restore process.');
108+
}
109+
break;
110+
111+
default:
112+
throw new DatabaseException('The database type in use is not yet supported.');
113+
}
114+
}
72115
}

src/Command/UpdateCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6868
$this->configuration->install($environment, $userInputs->getSettings());
6969

7070
$io->success('Environment successfully updated.');
71+
$io->info('If you changed the database service, please consider using the "origami database:reset" command.');
7172
}
7273
} catch (OrigamiExceptionInterface $exception) {
7374
$io->error($exception->getMessage());

src/Resources/docker-fragments/mariadb.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
database:
22
image: ${DOCKER_DATABASE_IMAGE}
33
environment:
4-
- MYSQL_ROOT_PASSWORD=YourPwdShouldBeLongAndSecure
5-
- MYSQL_DATABASE=origami
4+
- MARIADB_ROOT_PASSWORD=YourPwdShouldBeLongAndSecure
5+
- MARIADB_DATABASE=origami
66
ports:
77
- "3306:3306"
88
volumes:

src/Service/ApplicationContext.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,18 @@
1111
use App\Service\Wrapper\ProcessProxy;
1212
use App\ValueObject\EnvironmentEntity;
1313
use Symfony\Component\Console\Input\InputInterface;
14+
use Symfony\Component\Yaml\Yaml;
1415

1516
class ApplicationContext
1617
{
1718
private EnvironmentEntity $environment;
19+
private ?array $configuration = null;
1820

1921
public function __construct(
2022
private ApplicationData $applicationData,
2123
private ProcessProxy $processProxy,
22-
private Validator $validator
24+
private Validator $validator,
25+
private string $installDir,
2326
) {
2427
}
2528

@@ -75,6 +78,34 @@ public function getProjectName(): string
7578
return "{$this->environment->getType()}_{$this->environment->getName()}";
7679
}
7780

81+
/**
82+
* Retrieves the configuration from the environment "docker-compose.yml" file.
83+
*
84+
* @throws FilesystemException
85+
*
86+
* @return array[]
87+
*/
88+
public function getEnvironmentConfiguration(): array
89+
{
90+
if ($this->configuration) {
91+
return $this->configuration;
92+
}
93+
94+
$environment = $this->getActiveEnvironment();
95+
$configurationPath = $environment->getLocation().$this->installDir.'/docker-compose.yml';
96+
97+
if (!is_file($configurationPath)) {
98+
throw new FilesystemException(sprintf("Unable to find the file.\n%s", $configurationPath));
99+
}
100+
101+
$configuration = Yaml::parseFile($configurationPath);
102+
if (!\is_array($configuration)) {
103+
throw new FilesystemException(sprintf("Unable to parse the file content as YAML.\n%s", $configurationPath));
104+
}
105+
106+
return $this->configuration = $configuration;
107+
}
108+
78109
/**
79110
* Checks whether the environment has been installed and correctly configured.
80111
*

src/Service/Middleware/Binary/Docker.php

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,14 @@ public function removeDatabaseVolume(): bool
201201
/**
202202
* Executes the native MySQL dump process.
203203
*/
204-
public function dumpMysqlDatabase(string $path): bool
204+
public function dumpMysqlDatabase(string $username, string $password, string $path): bool
205205
{
206206
$environment = $this->applicationContext->getActiveEnvironment();
207207

208208
$command = str_replace(
209-
['{container}', '{password}', '{database}', '{filename}'],
210-
['$(docker compose ps --quiet database)', Database::DEFAULT_SERVICE_PASSWORD, Database::DEFAULT_SERVICE_DATABASE, $path],
211-
'docker exec --interactive {container} mysqldump --user=root --password={password} {database} > {filename}'
209+
['{container}', '{username}', '{password}', '{database}', '{filename}'],
210+
['$(docker compose ps --quiet database)', $username, $password, Database::DEFAULT_SERVICE_DATABASE, $path],
211+
'docker exec --interactive {container} mysqldump --user={username} --password={password} {database} > {filename}'
212212
);
213213
$environmentVariables = $this->getEnvironmentVariables($environment);
214214

@@ -218,14 +218,14 @@ public function dumpMysqlDatabase(string $path): bool
218218
/**
219219
* Executes the native Postgres dump process.
220220
*/
221-
public function dumpPostgresDatabase(string $path): bool
221+
public function dumpPostgresDatabase(string $username, string $password, string $path): bool
222222
{
223223
$environment = $this->applicationContext->getActiveEnvironment();
224224

225225
$command = str_replace(
226-
['{container}', '{password}', '{database}', '{filename}'],
227-
['$(docker compose ps --quiet database)', Database::DEFAULT_SERVICE_PASSWORD, Database::DEFAULT_SERVICE_DATABASE, $path],
228-
'docker exec --interactive {container} pg_dump --clean --dbname=postgresql://postgres:{password}@127.0.0.1:5432/{database} > {filename}'
226+
['{container}', '{username}', '{password}', '{database}', '{filename}'],
227+
['$(docker compose ps --quiet database)', $username, $password, Database::DEFAULT_SERVICE_DATABASE, $path],
228+
'docker exec --interactive {container} pg_dump --clean --dbname=postgresql://{username}:{password}@127.0.0.1:5432/{database} > {filename}'
229229
);
230230

231231
$environmentVariables = $this->getEnvironmentVariables($environment);
@@ -236,14 +236,14 @@ public function dumpPostgresDatabase(string $path): bool
236236
/**
237237
* Executes the native MySQL restore process.
238238
*/
239-
public function restoreMysqlDatabase(string $path): bool
239+
public function restoreMysqlDatabase(string $username, string $password, string $path): bool
240240
{
241241
$environment = $this->applicationContext->getActiveEnvironment();
242242

243243
$command = str_replace(
244-
['{container}', '{password}', '{database}', '{filename}'],
245-
['$(docker compose ps --quiet database)', Database::DEFAULT_SERVICE_PASSWORD, Database::DEFAULT_SERVICE_DATABASE, $path],
246-
'docker exec --interactive {container} mysql --user=root --password={password} {database} < {filename}'
244+
['{container}', '{username}', '{password}', '{database}', '{filename}'],
245+
['$(docker compose ps --quiet database)', $username, $password, Database::DEFAULT_SERVICE_DATABASE, $path],
246+
'docker exec --interactive {container} mysql --user={username} --password={password} {database} < {filename}'
247247
);
248248
$environmentVariables = $this->getEnvironmentVariables($environment);
249249

@@ -253,14 +253,14 @@ public function restoreMysqlDatabase(string $path): bool
253253
/**
254254
* Executes the native Postgres restore process.
255255
*/
256-
public function restorePostgresDatabase(string $path): bool
256+
public function restorePostgresDatabase(string $username, string $password, string $path): bool
257257
{
258258
$environment = $this->applicationContext->getActiveEnvironment();
259259

260260
$command = str_replace(
261-
['{container}', '{password}', '{database}', '{filename}'],
262-
['$(docker compose ps --quiet database)', Database::DEFAULT_SERVICE_PASSWORD, Database::DEFAULT_SERVICE_DATABASE, $path],
263-
'docker exec --interactive {container} psql --dbname=postgresql://postgres:{password}@127.0.0.1:5432/{database} < {filename}'
261+
['{container}', '{username}', '{password}', '{database}', '{filename}'],
262+
['$(docker compose ps --quiet database)', $username, $password, Database::DEFAULT_SERVICE_DATABASE, $path],
263+
'docker exec --interactive {container} psql --dbname=postgresql://{username}:{password}@127.0.0.1:5432/{database} < {filename}'
264264
);
265265
$environmentVariables = $this->getEnvironmentVariables($environment);
266266

0 commit comments

Comments
 (0)