0012728: install from (backup) dump
authorPhilipp Schüle <p.schuele@metaways.de>
Mon, 27 Feb 2017 11:51:35 +0000 (12:51 +0100)
committerPhilipp Schüle <p.schuele@metaways.de>
Thu, 9 Mar 2017 17:09:01 +0000 (18:09 +0100)
https://forge.tine20.org/view.php?id=12728

Change-Id: I8b338562b5de1270f6ca9fe5e53d40760d27bf57
Reviewed-on: http://gerrit.tine20.com/customers/4274
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
tests/setup/Setup/ControllerTest.php
tests/setup/files/2017-02-27-11-42-25/tine20_files.tar.bz2 [new file with mode: 0644]
tests/setup/files/2017-02-27-11-42-25/tine20_mysql.sql.bz2 [new file with mode: 0644]
tine20/Setup/Controller.php
tine20/Setup/Frontend/Cli.php
tine20/Setup/Server/Cli.php

index ecdab81..8115e39 100644 (file)
@@ -20,7 +20,7 @@ require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php'
 class Setup_ControllerTest extends PHPUnit_Framework_TestCase
 {
     /**
-     * @var Setup_Frontend_Json
+     * @var Setup_Controller
      */
     protected $_uit = null;
     
@@ -51,7 +51,16 @@ class Setup_ControllerTest extends PHPUnit_Framework_TestCase
             'adminPassword'         => $testCredentials['password'],
         ));
     }
-       
+
+    /**
+     * testLoginWithWrongUsernameAndPassword
+     */
+    public function testLoginWithWrongUsernameAndPassword()
+    {
+        $result = $this->_uit->login('unknown_user_xxyz', 'wrong_password');
+        $this->assertFalse($result);
+    }
+
     /**
      * test uninstall application and cache clearing
      *
@@ -213,7 +222,6 @@ class Setup_ControllerTest extends PHPUnit_Framework_TestCase
     
     /**
      * test install application
-     *
      */
     public function testInstallApplications()
     {
@@ -257,6 +265,33 @@ class Setup_ControllerTest extends PHPUnit_Framework_TestCase
     }
 
     /**
+     * test install applications from dump
+     *
+     * @see 0012728: install from (backup) dump
+     */
+    public function testInstallFromDump()
+    {
+        if ($this->_uit->isInstalled('Tinebase')) {
+            $this->_uninstallAllApplications();
+        }
+
+        $oldTinebaseId = '99a88a21e657b8365bf80ae867e9d06d1c355a39';
+        $options = array(
+            'backupDir' => dirname(__DIR__) . '/files/2017-02-27-11-42-25',
+            'db' => 1,
+            'files' => 1,
+        );
+        $result = $this->_uit->getInstance()->installFromDump($options);
+        $this->assertTrue($result);
+        $this->assertTrue($this->_uit->isInstalled('Addressbook'), 'Addressbook is not installed');
+        $tinebaseId = Tinebase_Application::getInstance()->getApplicationByName('Tinebase')->getId();
+        $this->assertNotEquals($oldTinebaseId, $tinebaseId);
+        $this->assertGreaterThan(40, Tinebase_Application::getInstance()->getApplicationTables($tinebaseId));
+
+        $this->_uninstallAllApplications();
+    }
+
+    /**
      * test update application
      *
      * @todo test real update process; currently this test case only tests updating an already uptodate application
@@ -266,7 +301,7 @@ class Setup_ControllerTest extends PHPUnit_Framework_TestCase
         $applications = new Tinebase_Record_RecordSet('Tinebase_Model_Application');
         $applications->addRecord(Tinebase_Application::getInstance()->getApplicationByName('ActiveSync'));
         $result = $this->_uit->updateApplications($applications);
-        $this->assertTrue(is_array($result)); //Setup_Controller::updateApplications just returns an array of messages and throws exceptions on failure
+        $this->assertTrue(is_array($result));
     }
 
     /**
@@ -282,16 +317,6 @@ class Setup_ControllerTest extends PHPUnit_Framework_TestCase
     }
     
     /**
-     * testLoginWithWrongUsernameAndPassword
-     *
-     */
-    public function testLoginWithWrongUsernameAndPassword()
-    {
-        $result = $this->_uit->login('unknown_user_xxyz', 'wrong_password');
-        $this->assertFalse($result);
-    }
-    
-    /**
      * uninstallAllApplications
      */
     protected function _uninstallAllApplications()
@@ -304,6 +329,7 @@ class Setup_ControllerTest extends PHPUnit_Framework_TestCase
      * installAllApplications
      *
      * @param array $_options
+     * @throws Setup_Exception
      */
     protected function _installAllApplications($_options = null)
     {
diff --git a/tests/setup/files/2017-02-27-11-42-25/tine20_files.tar.bz2 b/tests/setup/files/2017-02-27-11-42-25/tine20_files.tar.bz2
new file mode 100644 (file)
index 0000000..fd97d1c
Binary files /dev/null and b/tests/setup/files/2017-02-27-11-42-25/tine20_files.tar.bz2 differ
diff --git a/tests/setup/files/2017-02-27-11-42-25/tine20_mysql.sql.bz2 b/tests/setup/files/2017-02-27-11-42-25/tine20_mysql.sql.bz2
new file mode 100644 (file)
index 0000000..58f98cc
Binary files /dev/null and b/tests/setup/files/2017-02-27-11-42-25/tine20_mysql.sql.bz2 differ
index 9203a84..7e6895c 100644 (file)
@@ -409,14 +409,21 @@ class Setup_Controller
      * @param Tinebase_Record_RecordSet $_applications
      * @return  array   messages
      */
-    public function updateApplications(Tinebase_Record_RecordSet $_applications)
+    public function updateApplications(Tinebase_Record_RecordSet $_applications = null)
     {
+        if ($_applications === null) {
+            $_applications = Tinebase_Application::getInstance()->getApplications();
+        }
+
+        // we need to clone here because we would taint the app cache otherwise
+        $applications = clone($_applications);
+
         $this->_updatedApplications = 0;
         $smallestMajorVersion = NULL;
         $biggestMajorVersion = NULL;
         
         //find smallest major version
-        foreach ($_applications as $application) {
+        foreach ($applications as $application) {
             if ($smallestMajorVersion === NULL || $application->getMajorVersion() < $smallestMajorVersion) {
                 $smallestMajorVersion = $application->getMajorVersion();
             }
@@ -428,23 +435,23 @@ class Setup_Controller
         $messages = array();
         
         // update tinebase first (to biggest major version)
-        $tinebase = $_applications->filter('name', 'Tinebase')->getFirstRecord();
+        $tinebase = $applications->filter('name', 'Tinebase')->getFirstRecord();
         if (! empty($tinebase)) {
-            unset($_applications[$_applications->getIndexById($tinebase->getId())]);
+            unset($applications[$applications->getIndexById($tinebase->getId())]);
         
             list($major, $minor) = explode('.', $this->getSetupXml('Tinebase')->version[0]);
             Setup_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Updating Tinebase to version ' . $major . '.' . $minor);
             
             for ($majorVersion = $tinebase->getMajorVersion(); $majorVersion <= $major; $majorVersion++) {
-                $messages += $this->updateApplication($tinebase, $majorVersion);
+                $messages = array_merge($messages, $this->updateApplication($tinebase, $majorVersion));
             }
         }
             
         // update the rest
         for ($majorVersion = $smallestMajorVersion; $majorVersion <= $biggestMajorVersion; $majorVersion++) {
-            foreach ($_applications as $application) {
+            foreach ($applications as $application) {
                 if ($application->getMajorVersion() <= $majorVersion) {
-                    $messages += $this->updateApplication($application, $majorVersion);
+                    $messages = array_merge($messages, $this->updateApplication($application, $majorVersion));
                 }
             }
         }
@@ -1413,6 +1420,65 @@ class Setup_Controller
     }
 
     /**
+     * install tine from dump file
+     *
+     * @param $options
+     * @throws Setup_Exception
+     * @return boolean
+     */
+    public function installFromDump($options)
+    {
+        $this->_clearCache();
+
+        if ($this->isInstalled('Tinebase')) {
+            throw new Setup_Exception('Tinebase already installed!');
+        }
+
+        $mysqlBackupFile = $options['backupDir'] . '/tine20_mysql.sql.bz2';
+        if (! file_exists($mysqlBackupFile)) {
+            throw new Setup_Exception("$mysqlBackupFile not found");
+        }
+
+        Setup_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Installing from dump ' . $mysqlBackupFile);
+
+        $this->_replaceTinebaseidInDump($mysqlBackupFile);
+        $this->restore($options);
+        $this->updateApplications();
+
+        return true;
+    }
+
+    /**
+     * replace old Tinebase ID in dump to make sure we have a unique installation ID
+     *
+     * TODO: think about moving the Tinebase ID (and more info) to a metadata.json file in the backup zip
+     *
+     * @param $mysqlBackupFile
+     * @throws Setup_Exception
+     */
+    protected function _replaceTinebaseidInDump($mysqlBackupFile)
+    {
+        // fetch old Tinebase ID
+        $cmd = "bzcat $mysqlBackupFile | grep \",'Tinebase',\"";
+        $result = exec($cmd);
+        if (! preg_match("/'([0-9a-f]+)','Tinebase'/", $result, $matches)) {
+            throw new Setup_Exception('could not find Tinebase ID in dump');
+        }
+        $oldTinebaseId = $matches[1];
+
+        $cmd = "bzcat $mysqlBackupFile | sed s/"
+            . $oldTinebaseId . '/'
+            . Tinebase_Record_Abstract::generateUID() . "/g | " // g for global!
+            . "bzip2 > " . $mysqlBackupFile . '.tmp';
+
+        Setup_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $cmd);
+
+        exec($cmd);
+        copy($mysqlBackupFile . '.tmp', $mysqlBackupFile);
+        unlink($mysqlBackupFile . '.tmp');
+    }
+
+    /**
      * delete list of applications
      *
      * @param array $_applications list of application names
@@ -1878,6 +1944,8 @@ class Setup_Controller
         
         // clear cache
         $cache = Setup_Core::getCache()->clean(Zend_Cache::CLEANING_MODE_ALL);
+
+        Tinebase_Application::getInstance()->resetClassCache();
         
         // deactivate cache again
         Tinebase_Core::setupCache(FALSE);
@@ -2013,25 +2081,25 @@ class Setup_Controller
      *    )
      *
      * @param $options
-     * @throws Exception
+     * @throws Setup_Exception
      */
     public function restore($options)
     {
         if (! isset($options['backupDir'])) {
-            throw new Exception("you need to specify the backupDir");
+            throw new Setup_Exception("you need to specify the backupDir");
         }
 
         if (isset($options['config']) && $options['config']) {
             $configBackupFile = $options['backupDir']. '/tine20_config.tar.bz2';
             if (! file_exists($configBackupFile)) {
-                throw new Exception("$configBackupFile not found");
+                throw new Setup_Exception("$configBackupFile not found");
             }
 
             $configDir = isset($options['configDir']) ? $options['configDir'] : false;
             if (!$configDir) {
                 $configFile = stream_resolve_include_path('config.inc.php');
                 if (!$configFile) {
-                    throw new Exception("can't detect configDir, please use configDir option");
+                    throw new Setup_Exception("can't detect configDir, please use configDir option");
                 }
                 $configDir = dirname($configFile);
             }
@@ -2048,9 +2116,10 @@ class Setup_Controller
 
         $filesDir = isset($config->filesdir) ? $config->filesdir : false;
         if (isset($options['files']) && $options['files']) {
-            $filesBackupFile = $options['backupDir'] . '/tine20_files.tar.bz2';
+            $dir = $options['backupDir'];
+            $filesBackupFile = $dir . '/tine20_files.tar.bz2';
             if (! file_exists($filesBackupFile)) {
-                throw new Exception("$filesBackupFile not found");
+                throw new Setup_Exception("$filesBackupFile not found");
             }
 
             `cd $filesDir; tar xf $filesBackupFile`;
index 767363a..2d641aa 100644 (file)
@@ -56,6 +56,8 @@ class Setup_Frontend_Cli
             $result = $this->_update($_opts);
         } elseif(isset($_opts->uninstall)) {
             $this->_uninstall($_opts);
+        } elseif(isset($_opts->install_dump)) {
+            $this->_installDump($_opts);
         } elseif(isset($_opts->list)) {
             $result = $this->_listInstalled();
         } elseif(isset($_opts->sync_accounts_from_ldap)) {
@@ -332,15 +334,14 @@ class Setup_Frontend_Cli
         }
         
         //set rights
-        foreach ($applications as &$application) {
-            $newApplicationId = Tinebase_Application::getInstance()->getApplicationByName($application->name)->getId();
+        foreach ($applications as $app) {
+            $newApplicationId = Tinebase_Application::getInstance()->getApplicationByName($app->name)->getId();
             
             foreach ($rights as &$right) {
-                if ($right['application_id'] == $application->id) {
+                if ($right['application_id'] == $app->id) {
                     $right['application_id'] = $newApplicationId;
                 }
             }
-            
         }
         
         Tinebase_Acl_Roles::getInstance()->setRoleRights($users->getId(), $rights);
@@ -710,12 +711,20 @@ class Setup_Frontend_Cli
         return $adminRole;
     }
 
+    /**
+     * @param Zend_Console_Getopt $_opts
+     * @throws Exception
+     */
     protected function _backup(Zend_Console_Getopt $_opts)
     {
         $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
         Setup_Controller::getInstance()->backup($options);
     }
 
+    /**
+     * @param Zend_Console_Getopt $_opts
+     * @throws Exception
+     */
     protected function _restore(Zend_Console_Getopt $_opts)
     {
         $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
@@ -723,6 +732,16 @@ class Setup_Frontend_Cli
     }
 
     /**
+     * @param Zend_Console_Getopt $_opts
+     * @throws Exception
+     */
+    protected function _installDump(Zend_Console_Getopt $_opts)
+    {
+        $options = $this->_parseRemainingArgs($_opts->getRemainingArgs());
+        Setup_Controller::getInstance()->installFromDump($options);
+    }
+
+    /**
      * parse options
      * 
      * @param string $_value
index 5bf82ec..0749bc6 100644 (file)
@@ -43,6 +43,9 @@ class Setup_Server_Cli implements Tinebase_Server_Interface
                     . ' To add imap or smtp settings, append (for example) \' -- imap="host:mail.example.org,port:143,dbmail_host:localhost" smtp="ssl:tls"\'',
                 'update-s'                  => 'Update applications [All] or comma separated list',
                 'uninstall-s'               => 'Uninstall application [All] or comma separated list',
+                'install_dump'              => 'Install Tine from a backup
+                         Examples:
+                           setup.php --install_dump -- db=1 files=1 backupDir=/backup/tine20',
                 'list-s'                    => 'List installed applications',
                 'sync_accounts_from_ldap'   => 'Import user and groups from ldap',
                     'dbmailldap'            => 'Only usable with sync_accounts_from_ldap. Fetches dbmail email user data from LDAP.',
@@ -75,8 +78,9 @@ class Setup_Server_Cli implements Tinebase_Server_Interface
 
         if (count($opts->toArray()) === 0 || $opts->h || 
             (empty($opts->install) && 
-            empty($opts->update) && 
-            empty($opts->uninstall) && 
+            empty($opts->install_dump) &&
+            empty($opts->update) &&
+            empty($opts->uninstall) &&
             empty($opts->list) && 
             empty($opts->sync_accounts_from_ldap) && 
             empty($opts->sync_passwords_from_ldap) &&