diff --git a/README.md b/README.md index 30a635d..491751a 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ After you have edited the env.php file, run the following command immediately : bin/magento app:config:import ``` -## Manual Run +## Run upload manually You can also manually trigger the stripped database upload from the command line by running the following command : @@ -95,6 +95,21 @@ To do a full DB dump bin/magento wearejh:stripped-db-provider:upload-to-remote --full ``` +## Import a remote dump locally + +You can use the module to import a dump from S3 directly to your local. It will back up your local admin accounts and +reimport them + +``` +bin/magento wearejh:stripped-db-provider:wearejh:stripped-db-provider:import-from-remote PROJECT NAME +``` + +To skip admin accounts backup + +``` +bin/magento wearejh:stripped-db-provider:wearejh:stripped-db-provider:import-from-remote PROJECT NAME --no-admin-backup=1 +``` + ## Issues / Feature Request Please open github issues for any issues you encounter or feature requests you want to see. PRs are of course welcomed. diff --git a/src/Console/ImportFromRemoteCommand.php b/src/Console/ImportFromRemoteCommand.php index 73d4eea..2a87984 100644 --- a/src/Console/ImportFromRemoteCommand.php +++ b/src/Console/ImportFromRemoteCommand.php @@ -6,6 +6,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputArgument; use Jh\StrippedDbProvider\Model\DbFacade; @@ -13,7 +14,8 @@ class ImportFromRemoteCommand extends Command { - const ARGUMENT_PROJECT_NAME = 'source-project-name'; + private const ARGUMENT_PROJECT_NAME = 'source-project-name'; + private const OPTION_NO_ADMIN_ACCOUNT_BACKUP = 'no-admin-backup'; public function __construct( private DbFacade $dbFacade, @@ -34,6 +36,13 @@ protected function configure() InputArgument::REQUIRED, 'Source Project Name to import from eg. prod-stroustrup-workshop.' ); + $this->addOption( + self::OPTION_NO_ADMIN_ACCOUNT_BACKUP, + null, + InputOption::VALUE_OPTIONAL, + 'Set this flag to skip backup of local admin accounts', + false + ); } /** @@ -43,6 +52,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int { try { $sourceProjectMeta = new ProjectMeta($input->getArgument(self::ARGUMENT_PROJECT_NAME)); + $backupAdminAccounts = !$input->getOption(self::OPTION_NO_ADMIN_ACCOUNT_BACKUP); + $output->writeln('Downloading Database From Cloud Storage...'); $this->dbFacade->downloadDatabaseDump($sourceProjectMeta); $output->writeln(sprintf( @@ -51,15 +62,30 @@ protected function execute(InputInterface $input, OutputInterface $output): int )); $output->writeln('Uncompressing Database ...'); $this->dbFacade->uncompressDatabaseDump($sourceProjectMeta); - $output->writeln('Importing Database ...'); + $output->writeln('Dropping local tables ...'); + + if ($backupAdminAccounts) { + $output->writeln('Backing up local admin accounts ...'); + $this->dbFacade->backupLocalAdminAccounts($sourceProjectMeta); + } + + $output->writeln("Starting the Database import"); $this->dbFacade->importDatabaseDump($sourceProjectMeta); $output->writeln("Database successfully imported."); + + if ($backupAdminAccounts) { + $output->writeln('Restoring local admin accounts ...'); + $this->dbFacade->restoreLocalAdminAccountsFromBackup($sourceProjectMeta); + } } catch (\Exception $e) { $output->writeln("{$e->getMessage()}"); } finally { if (isset($sourceProjectMeta)) { $this->dbFacade->cleanUpLocalDumpFiles($sourceProjectMeta); } + if ($backupAdminAccounts) { + $this->dbFacade->cleanUpAdminAccountsBackup($sourceProjectMeta); + } } return 0; } diff --git a/src/Model/Db/DbAdminAccountsManager.php b/src/Model/Db/DbAdminAccountsManager.php new file mode 100644 index 0000000..da5fb4c --- /dev/null +++ b/src/Model/Db/DbAdminAccountsManager.php @@ -0,0 +1,77 @@ +config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_HOST); + $dbName = $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_NAME); + $dumper = new Mysqldump( + "mysql:host={$hostName};dbname={$dbName}", + $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_USER), + $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_PASSWORD), + [ + 'skip-definer' => true, + 'add-drop-table' => true, + 'include-tables' => [ + 'admin_passwords', + 'admin_system_messages', + 'admin_user', + 'admin_user_session', + 'authorization_role', + 'authorization_rule' + ] + ] + ); + + $dumper->start($this->getAdminAccountsBackupFilePath($projectMeta)); + } + + public function restoreAdminAccountsBackup(ProjectMeta $projectMeta): void + { + $hostName = $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_HOST); + $dbName = $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_NAME); + $dumper = new Mysqldump( + "mysql:host={$hostName};dbname={$dbName}", + $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_USER), + $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_PASSWORD), + ['skip-definer' => true] + ); + $dumper->restore($this->getAdminAccountsBackupFilePath($projectMeta)); + } + + public function cleanUp(ProjectMeta $projectMeta): void + { + try { + $this->shell->execute("rm %s", [$this->getAdminAccountsBackupFilePath($projectMeta)]); + } catch (\Exception $e) { + //empty + } + } + + private function getAdminAccountsBackupFilePath(ProjectMeta $projectMeta): string + { + return $projectMeta->getLocalDumpStoragePath() . self::BACKUP_FILENAME; + } +} diff --git a/src/Model/Db/DbDumper.php b/src/Model/Db/DbDumper.php index ccc7b9f..6d38519 100644 --- a/src/Model/Db/DbDumper.php +++ b/src/Model/Db/DbDumper.php @@ -50,7 +50,7 @@ public function dumpDb(ProjectMeta $projectMeta, bool $fullDump): void "mysql:host={$hostName};dbname={$dbName}", $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_USER), $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_PASSWORD), - ['skip-definer' => true] + ['skip-definer' => true, 'add-drop-table' => true, 'skip-triggers' => true] ); if (!$fullDump) { diff --git a/src/Model/Db/DbImporter.php b/src/Model/Db/DbImporter.php index bf1a4d7..9b5cc2b 100644 --- a/src/Model/Db/DbImporter.php +++ b/src/Model/Db/DbImporter.php @@ -4,7 +4,7 @@ namespace Jh\StrippedDbProvider\Model\Db; -use Magento\Framework\Shell; +use Ifsnop\Mysqldump\Mysqldump; use Magento\Framework\Config\ConfigOptionsListConstants; use Jh\StrippedDbProvider\Model\Config; use Jh\StrippedDbProvider\Model\ProjectMeta; @@ -12,8 +12,7 @@ class DbImporter { public function __construct( - private Config $config, - private Shell $shell + private Config $config ) { } @@ -23,15 +22,14 @@ public function __construct( */ public function importDatabase(ProjectMeta $projectMeta): void { - $cmd = sprintf( - "mysql -h%s -u%s --password=%s %s < %s", - $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_HOST), + $hostName = $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_HOST); + $dbName = $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_NAME); + $dumper = new Mysqldump( + "mysql:host={$hostName};dbname={$dbName}", $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_USER), $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_PASSWORD), - $this->config->getLocalDbConfigData(ConfigOptionsListConstants::KEY_NAME), - $projectMeta->getLocalAbsoluteFileDumpPath() + ['skip-definer' => true] ); - - $this->shell->execute($cmd); + $dumper->restore($projectMeta->getLocalAbsoluteFileDumpPath()); } } diff --git a/src/Model/DbFacade.php b/src/Model/DbFacade.php index 546a15b..7a6d225 100644 --- a/src/Model/DbFacade.php +++ b/src/Model/DbFacade.php @@ -14,13 +14,12 @@ public function __construct( private Db\DbDownloader $downloader, private Db\DbUploader $uploader, private Db\DbImporter $importer, - private Db\DbCleaner $cleaner + private Db\DbCleaner $cleaner, + private Db\DbAdminAccountsManager $adminAccountsManager ) { } /** - * @param ProjectMeta $projectMeta - * @param bool $fullDump * @throws \Exception */ public function dumpDatabase(ProjectMeta $projectMeta, bool $fullDump): void @@ -37,7 +36,6 @@ public function downloadDatabaseDump(ProjectMeta $projectMeta): void } /** - * @param ProjectMeta $projectMeta * @throws \Exception */ public function compressDatabaseDump(ProjectMeta $projectMeta): void @@ -46,7 +44,6 @@ public function compressDatabaseDump(ProjectMeta $projectMeta): void } /** - * @param ProjectMeta $projectMeta * @throws \Exception */ public function uncompressDatabaseDump(ProjectMeta $projectMeta): void @@ -55,7 +52,6 @@ public function uncompressDatabaseDump(ProjectMeta $projectMeta): void } /** - * @param ProjectMeta $projectMeta * @throws \Exception */ public function importDatabaseDump(ProjectMeta $projectMeta): void @@ -63,19 +59,28 @@ public function importDatabaseDump(ProjectMeta $projectMeta): void $this->importer->importDatabase($projectMeta); } - /** - * @param ProjectMeta $projectMeta - */ public function cleanUpLocalDumpFiles(ProjectMeta $projectMeta): void { $this->cleaner->cleanUp($projectMeta); } - /** - * @param ProjectMeta $projectMeta - */ public function uploadDatabaseDump(ProjectMeta $projectMeta): void { $this->uploader->uploadDBDump($projectMeta); } + + public function backupLocalAdminAccounts(ProjectMeta $projectMeta): void + { + $this->adminAccountsManager->backupAdminAccounts($projectMeta); + } + + public function restoreLocalAdminAccountsFromBackup(ProjectMeta $projectMeta): void + { + $this->adminAccountsManager->restoreAdminAccountsBackup($projectMeta); + } + + public function cleanUpAdminAccountsBackup(ProjectMeta $projectMeta): void + { + $this->adminAccountsManager->cleanUp($projectMeta); + } } diff --git a/src/Model/ProjectMeta.php b/src/Model/ProjectMeta.php index 3a6b8e0..5c3ba4c 100644 --- a/src/Model/ProjectMeta.php +++ b/src/Model/ProjectMeta.php @@ -58,4 +58,9 @@ public function getRemoteDumpObjectKey(): string { return $this->remoteStoragePath . $this->getCompressedDumpFileName(); } + + public function getLocalDumpStoragePath(): string + { + return $this->localStoragePath; + } }