-
Notifications
You must be signed in to change notification settings - Fork 180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Separate db tool and db backup/restore services for version 2.x #398
Changes from 2 commits
02e3518
fb40d39
c92e7e4
260d01c
5923bed
66e4390
831b101
817c8ee
1a71772
6379ac1
c355de3
7d2d90e
966681b
87d9f1a
b919a90
3cde299
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,15 @@ public function getConfigTreeBuilder(): TreeBuilder | |
$rootNode | ||
->children() | ||
->booleanNode('cache_sqlite_db')->defaultFalse()->end() | ||
->arrayNode('cache_db') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we then remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I'll remove it. |
||
->addDefaultsIfNotSet() | ||
->ignoreExtraKeys(false) | ||
->children() | ||
->scalarNode('sqlite') | ||
->defaultNull() | ||
->end() | ||
->end() | ||
->end() | ||
->scalarNode('command_verbosity')->defaultValue('normal')->end() | ||
->booleanNode('command_decoration')->defaultTrue()->end() | ||
->arrayNode('query') | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?xml version="1.0" ?> | ||
|
||
<container xmlns="http://symfony.com/schema/dic/services" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> | ||
|
||
<services> | ||
<service id="liip_functional_test.services.fixtures_loader_factory" class="Liip\FunctionalTestBundle\Services\FixturesLoaderFactory" public="true"> | ||
<argument type="service" id="service_container" /> | ||
</service> | ||
|
||
<service id="liip_functional_test.services_database_backup.sqlite" class="Liip\FunctionalTestBundle\Services\DatabaseBackup\SqliteDatabaseBackup" public="true"> | ||
<argument type="service" id="service_container" /> | ||
<argument type="service" id="liip_functional_test.services.fixtures_loader_factory" /> | ||
</service> | ||
|
||
<service id="liip_functional_test.services_database_backup.mysql_custom" class="Liip\FunctionalTestBundle\Services\DatabaseBackup\MysqlCustomDatabaseBackup" public="true"> | ||
<argument type="service" id="service_container" /> | ||
<argument type="service" id="liip_functional_test.services.fixtures_loader_factory" /> | ||
</service> | ||
|
||
<service id="liip_functional_test.services_database_tools.ormdatabase_tool" class="Liip\FunctionalTestBundle\Services\DatabaseTools\ORMDatabaseTool" public="false"> | ||
<argument type="service" id="service_container" /> | ||
<argument type="service" id="liip_functional_test.services.fixtures_loader_factory" /> | ||
</service> | ||
<service id="liip_functional_test.services_database_tools.ormsqllite_database_tool" class="Liip\FunctionalTestBundle\Services\DatabaseTools\ORMSqlliteDatabaseTool" public="false"> | ||
<argument type="service" id="service_container" /> | ||
<argument type="service" id="liip_functional_test.services.fixtures_loader_factory" /> | ||
</service> | ||
<service id="liip_functional_test.services_database_tools.mongo_dbdatabase_tool" class="Liip\FunctionalTestBundle\Services\DatabaseTools\MongoDBDatabaseTool" public="false"> | ||
<argument type="service" id="service_container" /> | ||
<argument type="service" id="liip_functional_test.services.fixtures_loader_factory" /> | ||
</service> | ||
<service id="liip_functional_test.services_database_tools.phpcrdatabase_tool" class="Liip\FunctionalTestBundle\Services\DatabaseTools\PHPCRDatabaseTool" public="false"> | ||
<argument type="service" id="service_container" /> | ||
<argument type="service" id="liip_functional_test.services.fixtures_loader_factory" /> | ||
</service> | ||
<service id="liip_functional_test.services.database_tool_collection" class="Liip\FunctionalTestBundle\Services\DatabaseToolCollection" public="true"> | ||
<argument type="service" id="service_container" /> | ||
<argument type="service" id="liip_functional_test.services.fixtures_loader_factory" /> | ||
<call method="add"> | ||
<argument type="service" id="liip_functional_test.services_database_tools.ormdatabase_tool" /> | ||
</call> | ||
<call method="add"> | ||
<argument type="service" id="liip_functional_test.services_database_tools.ormsqllite_database_tool" /> | ||
</call> | ||
<call method="add"> | ||
<argument type="service" id="liip_functional_test.services_database_tools.mongo_dbdatabase_tool" /> | ||
</call> | ||
<call method="add"> | ||
<argument type="service" id="liip_functional_test.services_database_tools.phpcrdatabase_tool" /> | ||
</call> | ||
</service> | ||
</services> | ||
</container> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Liip/FunctionalTestBundle | ||
* | ||
* (c) Lukas Kahwe Smith <smith@pooteeweet.org> | ||
* | ||
* This source file is subject to the MIT license that is bundled | ||
* with this source code in the file LICENSE. | ||
*/ | ||
|
||
namespace Liip\FunctionalTestBundle\Services\DatabaseBackup; | ||
|
||
use Doctrine\Common\DataFixtures\Executor\AbstractExecutor; | ||
use Doctrine\DBAL\Connection; | ||
use Liip\FunctionalTestBundle\Services\FixturesLoaderFactory; | ||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
|
||
/** | ||
* @author Aleksey Tupichenkov <alekseytupichenkov@gmail.com> | ||
*/ | ||
abstract class AbstractDatabaseBackup | ||
{ | ||
protected $container; | ||
|
||
protected $fixturesLoaderFactory; | ||
|
||
/** | ||
* @var Connection | ||
*/ | ||
protected $connection; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
protected $metadatas; | ||
|
||
/** | ||
* The fixture classnames. | ||
* | ||
* @var array | ||
*/ | ||
protected $classNames; | ||
|
||
public function __construct(ContainerInterface $container, FixturesLoaderFactory $fixturesLoaderFactory) | ||
{ | ||
$this->container = $container; | ||
$this->fixturesLoaderFactory = $fixturesLoaderFactory; | ||
} | ||
|
||
public function init(Connection $connection, array $metadatas, array $classNames): void | ||
{ | ||
$this->connection = $connection; | ||
$this->metadatas = $metadatas; | ||
$this->classNames = $classNames; | ||
} | ||
|
||
abstract public function getBackupName(): string; | ||
|
||
abstract public function isBackupActual(): bool; | ||
|
||
abstract public function backup(AbstractExecutor $executor): void; | ||
|
||
abstract public function restore(AbstractExecutor $executor): void; | ||
|
||
/** | ||
* Determine if the Fixtures that define a database backup have been | ||
* modified since the backup was made. | ||
* | ||
* @param string $backup The fixture backup SQLite database file path | ||
* | ||
* @return bool TRUE if the backup was made since the modifications to the | ||
* fixtures; FALSE otherwise | ||
*/ | ||
protected function isBackupUpToDate(string $backup): bool | ||
{ | ||
$backupLastModifiedDateTime = \DateTime::createFromFormat('U', filemtime($backup)); | ||
|
||
$loader = $this->fixturesLoaderFactory->getFixtureLoader($this->classNames); | ||
|
||
// Use loader in order to fetch all the dependencies fixtures. | ||
foreach ($loader->getFixtures() as $className) { | ||
$fixtureLastModifiedDateTime = $this->getFixtureLastModified($className); | ||
if ($backupLastModifiedDateTime < $fixtureLastModifiedDateTime) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* This function finds the time when the data blocks of a class definition | ||
* file were being written to, that is, the time when the content of the | ||
* file was changed. | ||
* | ||
* @param string $class The fully qualified class name of the fixture class to | ||
* check modification date on | ||
* | ||
* @return \DateTime|null | ||
*/ | ||
protected function getFixtureLastModified($class): ?\DateTime | ||
{ | ||
$lastModifiedDateTime = null; | ||
|
||
$reflClass = new \ReflectionClass($class); | ||
$classFileName = $reflClass->getFileName(); | ||
|
||
if (file_exists($classFileName)) { | ||
$lastModifiedDateTime = new \DateTime(); | ||
$lastModifiedDateTime->setTimestamp(filemtime($classFileName)); | ||
} | ||
|
||
return $lastModifiedDateTime; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Liip/FunctionalTestBundle | ||
* | ||
* (c) Lukas Kahwe Smith <smith@pooteeweet.org> | ||
* | ||
* This source file is subject to the MIT license that is bundled | ||
* with this source code in the file LICENSE. | ||
*/ | ||
|
||
namespace Liip\FunctionalTestBundle\Services\DatabaseBackup; | ||
|
||
use Doctrine\Common\DataFixtures\Executor\AbstractExecutor; | ||
|
||
/** | ||
* @author Aleksey Tupichenkov <alekseytupichenkov@gmail.com> | ||
* | ||
* It's class created just for example that how to create database backup/restore service | ||
*/ | ||
class MysqlCustomDatabaseBackup extends AbstractDatabaseBackup | ||
{ | ||
public function getBackupName(): string | ||
{ | ||
return $this->container->getParameter('kernel.cache_dir').'/test_mysql_'.md5(serialize($this->metadatas).serialize($this->classNames)).'.sql'; | ||
} | ||
|
||
public function isBackupActual(): bool | ||
{ | ||
$backupDBFileName = $this->getBackupName(); | ||
$backupReferenceFileName = $backupDBFileName.'.ser'; | ||
|
||
return file_exists($backupDBFileName) && file_exists($backupReferenceFileName) && $this->isBackupUpToDate($backupDBFileName); | ||
} | ||
|
||
public function backup(AbstractExecutor $executor): void | ||
{ | ||
$params = $this->connection->getParams(); | ||
if (isset($params['master'])) { | ||
$params = $params['master']; | ||
} | ||
|
||
$dbName = isset($params['dbname']) ? $params['dbname'] : ''; | ||
$dbHost = isset($params['host']) ? $params['host'] : ''; | ||
$dbPort = isset($params['port']) ? $params['port'] : ''; | ||
$dbUser = isset($params['user']) ? $params['user'] : ''; | ||
$dbPass = isset($params['password']) ? $params['password'] : ''; | ||
|
||
$executor->getReferenceRepository()->save($this->getBackupName()); | ||
|
||
exec("mysqldump -h $dbHost -u $dbUser -p$dbPass $dbName > {$this->getBackupName()}"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the MySQL database is in a Docker container, this command won't work? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I use docker too, it will works fine if you install mysql-client in container with symfony app :) |
||
} | ||
|
||
public function restore(AbstractExecutor $executor): void | ||
{ | ||
$params = $this->connection->getParams(); | ||
if (isset($params['master'])) { | ||
$params = $params['master']; | ||
} | ||
|
||
$dbName = isset($params['dbname']) ? $params['dbname'] : ''; | ||
$dbHost = isset($params['host']) ? $params['host'] : ''; | ||
$dbPort = isset($params['port']) ? $params['port'] : ''; | ||
$dbUser = isset($params['user']) ? $params['user'] : ''; | ||
$dbPass = isset($params['password']) ? $params['password'] : ''; | ||
|
||
exec("mysqladmin -h $dbHost -u $dbUser -p$dbPass drop $dbName -f"); | ||
exec("mysqladmin -h $dbHost -u $dbUser -p$dbPass create $dbName"); | ||
exec("mysql -h $dbHost -u $dbUser -p$dbPass $dbName < {$this->getBackupName()}"); | ||
|
||
$executor->getReferenceRepository()->load($this->getBackupName()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Liip/FunctionalTestBundle | ||
* | ||
* (c) Lukas Kahwe Smith <smith@pooteeweet.org> | ||
* | ||
* This source file is subject to the MIT license that is bundled | ||
* with this source code in the file LICENSE. | ||
*/ | ||
|
||
namespace Liip\FunctionalTestBundle\Services\DatabaseBackup; | ||
|
||
use Doctrine\Common\DataFixtures\Executor\AbstractExecutor; | ||
|
||
/** | ||
* @author Aleksey Tupichenkov <alekseytupichenkov@gmail.com> | ||
*/ | ||
class SqliteDatabaseBackup extends AbstractDatabaseBackup | ||
{ | ||
public function getBackupName(): string | ||
{ | ||
return $this->container->getParameter('kernel.cache_dir').'/test_sqllite_'.md5(serialize($this->metadatas).serialize($this->classNames)).'.db'; | ||
} | ||
|
||
private function getDatabaseName(): string | ||
{ | ||
$params = $this->connection->getParams(); | ||
if (isset($params['master'])) { | ||
$params = $params['master']; | ||
} | ||
|
||
$name = $params['path'] ?? ($params['dbname'] ?? false); | ||
if (!$name) { | ||
throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped."); | ||
} | ||
|
||
return $name; | ||
} | ||
|
||
public function isBackupActual(): bool | ||
{ | ||
$backupDBFileName = $this->getBackupName(); | ||
$backupReferenceFileName = $backupDBFileName.'.ser'; | ||
|
||
return file_exists($backupDBFileName) && file_exists($backupReferenceFileName) && $this->isBackupUpToDate($backupDBFileName); | ||
} | ||
|
||
public function backup(AbstractExecutor $executor): void | ||
{ | ||
$executor->getReferenceRepository()->save($this->getBackupName()); | ||
copy($this->getDatabaseName(), $this->getBackupName()); | ||
} | ||
|
||
public function restore(AbstractExecutor $executor): void | ||
{ | ||
copy($this->getBackupName(), $this->getDatabaseName()); | ||
$executor->getReferenceRepository()->load($this->getBackupName()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sqlite
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's typo