Skip to content

Commit

Permalink
#12 : Add database:dump and variable:add commands
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas43000 committed Nov 20, 2020
1 parent 8d73483 commit f4bc6e2
Show file tree
Hide file tree
Showing 9 changed files with 335 additions and 7 deletions.
10 changes: 9 additions & 1 deletion bin/kloud
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ $app->addCommands([
Command\Environment\Variable\SetCommand::$defaultName,
)),

(new Command\Environment\Variable\AddCommand(
Command\Environment\Variable\AddCommand::$defaultName,
)),

(new Command\Environment\Variable\UnsetCommand(
Command\Environment\Variable\UnsetCommand::$defaultName,
)),
Expand All @@ -95,7 +99,6 @@ $app->addCommands([
$app,
)),


(new Command\Environment\StopCommand(
Command\Environment\StopCommand::$defaultName,
$app,
Expand All @@ -110,6 +113,11 @@ $app->addCommands([
Command\Environment\Cache\ClearCommand::$defaultName,
$app,
)),

(new Command\Environment\Database\DumpCommand(
Command\Environment\Database\DumpCommand::$defaultName,
$app,
)),
]);

$app->run(new ArgvInput($argv), new ConsoleOutput());
8 changes: 6 additions & 2 deletions src/Domain/Environment/DTO/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
class Context implements NormalizableInterface, DenormalizableInterface
{
public ?Deployment $deployment;
public ?Database $database;
public iterable $environmentVariables;

public function __construct(?Deployment $deployment = null)
public function __construct(?Deployment $deployment = null, ?Database $database = null)
{
$this->deployment = $deployment;
$this->database = $database;
$this->environmentVariables = [];
}

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

public function setVariable(EnvironmentVariableInterface $newVariable)
public function setVariable(EnvironmentVariableInterface $newVariable): void
{
$i = 0;
foreach ($this->environmentVariables as $variable) {
Expand All @@ -56,6 +58,7 @@ public function setVariable(EnvironmentVariableInterface $newVariable)
public function denormalize(DenormalizerInterface $denormalizer, $data, string $format = null, array $context = [])
{
$this->deployment = $denormalizer->denormalize($data['deployment'], Deployment::class, $format, $context);
$this->database = $denormalizer->denormalize($data['database'], Database::class, $format, $context);
$this->environmentVariables = [];

$parser = new ExpressionParser();
Expand All @@ -82,6 +85,7 @@ public function normalize(NormalizerInterface $normalizer, string $format = null
{
return [
'deployment' => $normalizer->normalize($this->deployment, $format, $context),
'database' => $normalizer->normalize($this->database, $format, $context),
'environment' => iterator_to_array((function ($variables) {
/** @var EnvironmentVariableInterface $variable */
foreach ($variables as $variable) {
Expand Down
19 changes: 19 additions & 0 deletions src/Domain/Environment/DTO/Database.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Kiboko\Cloud\Domain\Environment\DTO;

class Database
{
public ?string $databaseName;
public ?string $username;
public ?string $password;

public function __construct(?string $databaseName = null, ?string $username = null, ?string $password = null)
{
$this->databaseName = $databaseName;
$this->username = $username;
$this->password = $password;
}
}
4 changes: 2 additions & 2 deletions src/Domain/Environment/DTO/Deployment.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

class Deployment
{
public Server $server;
public string $path;
public ?Server $server;
public ?string $path;

public function __construct(?Server $server = null, ?string $path = null)
{
Expand Down
3 changes: 3 additions & 0 deletions src/Domain/Environment/DTO/DirectValueEnvironmentVariable.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ public function getVariable(): Variable
return $this->variable;
}

/**
* @return int|Expression|Variable|string
*/
public function getValue()
{
return $this->value;
Expand Down
178 changes: 178 additions & 0 deletions src/Platform/Console/Command/Environment/Database/DumpCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php

declare(strict_types=1);

namespace Kiboko\Cloud\Platform\Console\Command\Environment\Database;

use Deployer\Console\Application;
use Deployer\Deployer;
use Deployer\Host\Host;
use Deployer\Logger\Handler\FileHandler;
use Deployer\Logger\Handler\NullHandler;
use Deployer\Logger\Logger;
use Kiboko\Cloud\Domain\Environment\DTO\Context as EnvironmentContext;
use Kiboko\Cloud\Domain\Stack\DTO\Context as StackContext;
use Kiboko\Cloud\Platform\Console\EnvironmentWizard;
use Symfony\Component\Console\Application as Console;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Process\Process;
use Symfony\Component\Serializer\Encoder\YamlEncoder;
use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
use Symfony\Component\Serializer\Serializer;

final class DumpCommand extends Command
{
public static $defaultName = 'environment:database:dump';

private Console $console;
private EnvironmentWizard $wizard;

public function __construct(?string $name, Console $console)
{
$this->console = $console;
$this->wizard = new EnvironmentWizard();
parent::__construct($name);
}

protected function configure()
{
$this->setDescription('Dumps the database in the current state');

$this->wizard->configureConsoleCommand($this);
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$workingDirectory = $input->getOption('working-directory') ?: getcwd();

$finder = (new Finder())
->files()
->ignoreDotFiles(false)
->in($workingDirectory);

$format = new SymfonyStyle($input, $output);

$serializer = new Serializer(
[
new CustomNormalizer(),
new PropertyNormalizer(),
],
[
new YamlEncoder(),
]
);

if ($finder->hasResults()) {
foreach ($finder->name('/^\.?kloud.environment.ya?ml$/') as $environmentFile) {
try {
/** @var EnvironmentContext $environementContext */
$environementContext = $serializer->deserialize($environmentFile->getContents(), EnvironmentContext::class, 'yaml');
} catch (\Throwable $exception) {
$format->error($exception->getMessage());
continue;
}

break;
}
foreach ($finder->name('/^\.?kloud.ya?ml$/') as $stackFile) {
try {
/** @var StackContext $stackContext */
$stackContext = $serializer->deserialize($stackFile->getContents(), StackContext::class, 'yaml');
} catch (\Throwable $exception) {
$format->error($exception->getMessage());
continue;
}

break;
}
}

if (!isset($environementContext)) {
$format->error('No .kloud.environment.yaml file found in your directory. You must initialize it using environment:init command');

return 1;
}

$application = new Application($this->console->getName());
$deployer = new Deployer($application);
$deployer['output'] = $output;
$deployer['log_handler'] = function ($deployer) {
return !empty($deployer->config['log_file'])
? new FileHandler($deployer->config['log_file'])
: new NullHandler();
};
$deployer['logger'] = function ($deployer) {
return new Logger($deployer['log_handler']);
};

$host = new Host($environementContext->deployment->server->hostname);
$host->port($environementContext->deployment->server->port);
$host->user($environementContext->deployment->server->username);

$directories = explode('/', $workingDirectory);
$projectName = end($directories);
$remoteProjectPath = $environementContext->deployment->path.'/'.$projectName;

$sqlService = $format->askQuestion(new Question('What is the name of your SQL service?', 'sql'));
$process = new Process(['ssh', '-t', $host->getUser().'@'.$host->getHostname(), 'cd', $remoteProjectPath, '&&', 'docker-compose', 'ps', '-q', $sqlService]);

try {
$process->mustRun();
$containerIds = rtrim($process->getOutput(), PHP_EOL);
} catch (\Exception $exception) {
$format->error($exception->getMessage());

return 1;
}

if (!empty($stackContext->dbms)) {
$dbms = $stackContext->dbms;
} else {
$dbms = strtolower($format->askQuestion(new ChoiceQuestion('Is it a MySQL or PostgreSQL database?', ['MySQL', 'PostgreSQL'])));
}

$dumpName = $format->askQuestion(new Question('How do you want to name it?', 'dump.sql'));
$dumpPath = $remoteProjectPath.'/.docker/'.$dumpName;
$databaseName = $environementContext->database->databaseName;
$username = $environementContext->database->username;
$password = $environementContext->database->password;

if ('postgresql' === $dbms) {
$process2 = new Process(['ssh', '-t', $host->getUser().'@'.$host->getHostname(), 'docker', 'exec', '-i', $containerIds, 'pg_dump', '-U', $username, $databaseName, '>', $dumpPath]);
try {
$process2->mustRun();
$format->success('Dump well created at '.$host->getUser().'@'.$host->getHostname().':'.$dumpPath);

return 0;
} catch (\Exception $exception) {
$format->error($exception->getMessage());

return 1;
}
} else {
$process2 = new Process(['ssh', '-t', $host->getUser().'@'.$host->getHostname(), 'docker', 'exec', $containerIds, '/usr/bin/mysqldump', '-u', $username, '--password='.$password, $databaseName, '>', $dumpPath]);
try {
$process2->mustRun();
if (!file_exists($host->getUser().'@'.$host->getHostname().':'.$dumpPath)) {
$format->error('Dump couldn\'t be created');

return 1;
}
$format->success('Dump well created at '.$host->getUser().'@'.$host->getHostname().':'.$dumpPath);

return 0;
} catch (\Exception $exception) {
$format->error($exception->getMessage());

return 1;
}
}
}
}
5 changes: 3 additions & 2 deletions src/Platform/Console/Command/Environment/DestroyCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,11 @@ protected function execute(InputInterface $input, OutputInterface $output)

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

$commands = [
'docker:down' => 'cd '.$context->deployment->path.'/'.$projectName.' && docker-compose down -v',
'directory:remove' => 'cd '.$context->deployment->path.' && rm -rf '.$projectName,
'docker:down' => $cd.'/'.$projectName.' && docker-compose down -v',
'directory:remove' => $cd.' && rm -rf '.$projectName,
];

foreach ($commands as $key => $value) {
Expand Down
6 changes: 6 additions & 0 deletions src/Platform/Console/Command/Environment/InitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Kiboko\Cloud\Platform\Console\Command\Environment;

use Kiboko\Cloud\Domain\Environment\DTO\Context;
use Kiboko\Cloud\Domain\Environment\DTO\Database;
use Kiboko\Cloud\Domain\Environment\DTO\Deployment;
use Kiboko\Cloud\Domain\Environment\DTO\DirectValueEnvironmentVariable;
use Kiboko\Cloud\Domain\Environment\DTO\SecretValueEnvironmentVariable;
Expand Down Expand Up @@ -83,6 +84,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
),
$format->askQuestion(new Question('Please provide the path to your remote environment')),
),
new Database(
$format->askQuestion(new Question('Please provide the name of your database')),
$format->askQuestion(new Question('Please provide the user\'s name of your database')),
$format->askQuestion(new Question('Please provide the user\'s password of your database')),
),
);

$envDistPath = getcwd().'/.env.dist';
Expand Down
Loading

0 comments on commit f4bc6e2

Please sign in to comment.