Skip to content

Commit f4bc6e2

Browse files
committed
#12 : Add database:dump and variable:add commands
1 parent 8d73483 commit f4bc6e2

File tree

9 files changed

+335
-7
lines changed

9 files changed

+335
-7
lines changed

Diff for: bin/kloud

+9-1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ $app->addCommands([
7676
Command\Environment\Variable\SetCommand::$defaultName,
7777
)),
7878

79+
(new Command\Environment\Variable\AddCommand(
80+
Command\Environment\Variable\AddCommand::$defaultName,
81+
)),
82+
7983
(new Command\Environment\Variable\UnsetCommand(
8084
Command\Environment\Variable\UnsetCommand::$defaultName,
8185
)),
@@ -95,7 +99,6 @@ $app->addCommands([
9599
$app,
96100
)),
97101

98-
99102
(new Command\Environment\StopCommand(
100103
Command\Environment\StopCommand::$defaultName,
101104
$app,
@@ -110,6 +113,11 @@ $app->addCommands([
110113
Command\Environment\Cache\ClearCommand::$defaultName,
111114
$app,
112115
)),
116+
117+
(new Command\Environment\Database\DumpCommand(
118+
Command\Environment\Database\DumpCommand::$defaultName,
119+
$app,
120+
)),
113121
]);
114122

115123
$app->run(new ArgvInput($argv), new ConsoleOutput());

Diff for: src/Domain/Environment/DTO/Context.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
class Context implements NormalizableInterface, DenormalizableInterface
1414
{
1515
public ?Deployment $deployment;
16+
public ?Database $database;
1617
public iterable $environmentVariables;
1718

18-
public function __construct(?Deployment $deployment = null)
19+
public function __construct(?Deployment $deployment = null, ?Database $database = null)
1920
{
2021
$this->deployment = $deployment;
22+
$this->database = $database;
2123
$this->environmentVariables = [];
2224
}
2325

@@ -39,7 +41,7 @@ public function getVariable(string $variableName): EnvironmentVariableInterface
3941
throw new VariableNotFoundException(strtr('The variable %name% does not exist.', ['%name%' => $variableName]));
4042
}
4143

42-
public function setVariable(EnvironmentVariableInterface $newVariable)
44+
public function setVariable(EnvironmentVariableInterface $newVariable): void
4345
{
4446
$i = 0;
4547
foreach ($this->environmentVariables as $variable) {
@@ -56,6 +58,7 @@ public function setVariable(EnvironmentVariableInterface $newVariable)
5658
public function denormalize(DenormalizerInterface $denormalizer, $data, string $format = null, array $context = [])
5759
{
5860
$this->deployment = $denormalizer->denormalize($data['deployment'], Deployment::class, $format, $context);
61+
$this->database = $denormalizer->denormalize($data['database'], Database::class, $format, $context);
5962
$this->environmentVariables = [];
6063

6164
$parser = new ExpressionParser();
@@ -82,6 +85,7 @@ public function normalize(NormalizerInterface $normalizer, string $format = null
8285
{
8386
return [
8487
'deployment' => $normalizer->normalize($this->deployment, $format, $context),
88+
'database' => $normalizer->normalize($this->database, $format, $context),
8589
'environment' => iterator_to_array((function ($variables) {
8690
/** @var EnvironmentVariableInterface $variable */
8791
foreach ($variables as $variable) {

Diff for: src/Domain/Environment/DTO/Database.php

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Kiboko\Cloud\Domain\Environment\DTO;
6+
7+
class Database
8+
{
9+
public ?string $databaseName;
10+
public ?string $username;
11+
public ?string $password;
12+
13+
public function __construct(?string $databaseName = null, ?string $username = null, ?string $password = null)
14+
{
15+
$this->databaseName = $databaseName;
16+
$this->username = $username;
17+
$this->password = $password;
18+
}
19+
}

Diff for: src/Domain/Environment/DTO/Deployment.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
class Deployment
88
{
9-
public Server $server;
10-
public string $path;
9+
public ?Server $server;
10+
public ?string $path;
1111

1212
public function __construct(?Server $server = null, ?string $path = null)
1313
{

Diff for: src/Domain/Environment/DTO/DirectValueEnvironmentVariable.php

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public function getVariable(): Variable
2121
return $this->variable;
2222
}
2323

24+
/**
25+
* @return int|Expression|Variable|string
26+
*/
2427
public function getValue()
2528
{
2629
return $this->value;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Kiboko\Cloud\Platform\Console\Command\Environment\Database;
6+
7+
use Deployer\Console\Application;
8+
use Deployer\Deployer;
9+
use Deployer\Host\Host;
10+
use Deployer\Logger\Handler\FileHandler;
11+
use Deployer\Logger\Handler\NullHandler;
12+
use Deployer\Logger\Logger;
13+
use Kiboko\Cloud\Domain\Environment\DTO\Context as EnvironmentContext;
14+
use Kiboko\Cloud\Domain\Stack\DTO\Context as StackContext;
15+
use Kiboko\Cloud\Platform\Console\EnvironmentWizard;
16+
use Symfony\Component\Console\Application as Console;
17+
use Symfony\Component\Console\Command\Command;
18+
use Symfony\Component\Console\Input\InputInterface;
19+
use Symfony\Component\Console\Output\OutputInterface;
20+
use Symfony\Component\Console\Question\ChoiceQuestion;
21+
use Symfony\Component\Console\Question\Question;
22+
use Symfony\Component\Console\Style\SymfonyStyle;
23+
use Symfony\Component\Finder\Finder;
24+
use Symfony\Component\Process\Process;
25+
use Symfony\Component\Serializer\Encoder\YamlEncoder;
26+
use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
27+
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
28+
use Symfony\Component\Serializer\Serializer;
29+
30+
final class DumpCommand extends Command
31+
{
32+
public static $defaultName = 'environment:database:dump';
33+
34+
private Console $console;
35+
private EnvironmentWizard $wizard;
36+
37+
public function __construct(?string $name, Console $console)
38+
{
39+
$this->console = $console;
40+
$this->wizard = new EnvironmentWizard();
41+
parent::__construct($name);
42+
}
43+
44+
protected function configure()
45+
{
46+
$this->setDescription('Dumps the database in the current state');
47+
48+
$this->wizard->configureConsoleCommand($this);
49+
}
50+
51+
protected function execute(InputInterface $input, OutputInterface $output)
52+
{
53+
$workingDirectory = $input->getOption('working-directory') ?: getcwd();
54+
55+
$finder = (new Finder())
56+
->files()
57+
->ignoreDotFiles(false)
58+
->in($workingDirectory);
59+
60+
$format = new SymfonyStyle($input, $output);
61+
62+
$serializer = new Serializer(
63+
[
64+
new CustomNormalizer(),
65+
new PropertyNormalizer(),
66+
],
67+
[
68+
new YamlEncoder(),
69+
]
70+
);
71+
72+
if ($finder->hasResults()) {
73+
foreach ($finder->name('/^\.?kloud.environment.ya?ml$/') as $environmentFile) {
74+
try {
75+
/** @var EnvironmentContext $environementContext */
76+
$environementContext = $serializer->deserialize($environmentFile->getContents(), EnvironmentContext::class, 'yaml');
77+
} catch (\Throwable $exception) {
78+
$format->error($exception->getMessage());
79+
continue;
80+
}
81+
82+
break;
83+
}
84+
foreach ($finder->name('/^\.?kloud.ya?ml$/') as $stackFile) {
85+
try {
86+
/** @var StackContext $stackContext */
87+
$stackContext = $serializer->deserialize($stackFile->getContents(), StackContext::class, 'yaml');
88+
} catch (\Throwable $exception) {
89+
$format->error($exception->getMessage());
90+
continue;
91+
}
92+
93+
break;
94+
}
95+
}
96+
97+
if (!isset($environementContext)) {
98+
$format->error('No .kloud.environment.yaml file found in your directory. You must initialize it using environment:init command');
99+
100+
return 1;
101+
}
102+
103+
$application = new Application($this->console->getName());
104+
$deployer = new Deployer($application);
105+
$deployer['output'] = $output;
106+
$deployer['log_handler'] = function ($deployer) {
107+
return !empty($deployer->config['log_file'])
108+
? new FileHandler($deployer->config['log_file'])
109+
: new NullHandler();
110+
};
111+
$deployer['logger'] = function ($deployer) {
112+
return new Logger($deployer['log_handler']);
113+
};
114+
115+
$host = new Host($environementContext->deployment->server->hostname);
116+
$host->port($environementContext->deployment->server->port);
117+
$host->user($environementContext->deployment->server->username);
118+
119+
$directories = explode('/', $workingDirectory);
120+
$projectName = end($directories);
121+
$remoteProjectPath = $environementContext->deployment->path.'/'.$projectName;
122+
123+
$sqlService = $format->askQuestion(new Question('What is the name of your SQL service?', 'sql'));
124+
$process = new Process(['ssh', '-t', $host->getUser().'@'.$host->getHostname(), 'cd', $remoteProjectPath, '&&', 'docker-compose', 'ps', '-q', $sqlService]);
125+
126+
try {
127+
$process->mustRun();
128+
$containerIds = rtrim($process->getOutput(), PHP_EOL);
129+
} catch (\Exception $exception) {
130+
$format->error($exception->getMessage());
131+
132+
return 1;
133+
}
134+
135+
if (!empty($stackContext->dbms)) {
136+
$dbms = $stackContext->dbms;
137+
} else {
138+
$dbms = strtolower($format->askQuestion(new ChoiceQuestion('Is it a MySQL or PostgreSQL database?', ['MySQL', 'PostgreSQL'])));
139+
}
140+
141+
$dumpName = $format->askQuestion(new Question('How do you want to name it?', 'dump.sql'));
142+
$dumpPath = $remoteProjectPath.'/.docker/'.$dumpName;
143+
$databaseName = $environementContext->database->databaseName;
144+
$username = $environementContext->database->username;
145+
$password = $environementContext->database->password;
146+
147+
if ('postgresql' === $dbms) {
148+
$process2 = new Process(['ssh', '-t', $host->getUser().'@'.$host->getHostname(), 'docker', 'exec', '-i', $containerIds, 'pg_dump', '-U', $username, $databaseName, '>', $dumpPath]);
149+
try {
150+
$process2->mustRun();
151+
$format->success('Dump well created at '.$host->getUser().'@'.$host->getHostname().':'.$dumpPath);
152+
153+
return 0;
154+
} catch (\Exception $exception) {
155+
$format->error($exception->getMessage());
156+
157+
return 1;
158+
}
159+
} else {
160+
$process2 = new Process(['ssh', '-t', $host->getUser().'@'.$host->getHostname(), 'docker', 'exec', $containerIds, '/usr/bin/mysqldump', '-u', $username, '--password='.$password, $databaseName, '>', $dumpPath]);
161+
try {
162+
$process2->mustRun();
163+
if (!file_exists($host->getUser().'@'.$host->getHostname().':'.$dumpPath)) {
164+
$format->error('Dump couldn\'t be created');
165+
166+
return 1;
167+
}
168+
$format->success('Dump well created at '.$host->getUser().'@'.$host->getHostname().':'.$dumpPath);
169+
170+
return 0;
171+
} catch (\Exception $exception) {
172+
$format->error($exception->getMessage());
173+
174+
return 1;
175+
}
176+
}
177+
}
178+
}

Diff for: src/Platform/Console/Command/Environment/DestroyCommand.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
104104

105105
$directories = explode('/', $workingDirectory);
106106
$projectName = end($directories);
107+
$cd = 'cd '.$context->deployment->path;
107108

108109
$commands = [
109-
'docker:down' => 'cd '.$context->deployment->path.'/'.$projectName.' && docker-compose down -v',
110-
'directory:remove' => 'cd '.$context->deployment->path.' && rm -rf '.$projectName,
110+
'docker:down' => $cd.'/'.$projectName.' && docker-compose down -v',
111+
'directory:remove' => $cd.' && rm -rf '.$projectName,
111112
];
112113

113114
foreach ($commands as $key => $value) {

Diff for: src/Platform/Console/Command/Environment/InitCommand.php

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Kiboko\Cloud\Platform\Console\Command\Environment;
66

77
use Kiboko\Cloud\Domain\Environment\DTO\Context;
8+
use Kiboko\Cloud\Domain\Environment\DTO\Database;
89
use Kiboko\Cloud\Domain\Environment\DTO\Deployment;
910
use Kiboko\Cloud\Domain\Environment\DTO\DirectValueEnvironmentVariable;
1011
use Kiboko\Cloud\Domain\Environment\DTO\SecretValueEnvironmentVariable;
@@ -83,6 +84,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
8384
),
8485
$format->askQuestion(new Question('Please provide the path to your remote environment')),
8586
),
87+
new Database(
88+
$format->askQuestion(new Question('Please provide the name of your database')),
89+
$format->askQuestion(new Question('Please provide the user\'s name of your database')),
90+
$format->askQuestion(new Question('Please provide the user\'s password of your database')),
91+
),
8692
);
8793

8894
$envDistPath = getcwd().'/.env.dist';

0 commit comments

Comments
 (0)