diff --git a/core/Command/Maintenance/Repair.php b/core/Command/Maintenance/Repair.php index a1a75099214e..f3552c1b672a 100644 --- a/core/Command/Maintenance/Repair.php +++ b/core/Command/Maintenance/Repair.php @@ -26,7 +26,9 @@ namespace OC\Core\Command\Maintenance; use Exception; +use OCP\App\IAppManager; use OCP\IConfig; +use OCP\Migration\IRuntimeRepairStep; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; @@ -46,15 +48,18 @@ class Repair extends Command { private $progress; /** @var OutputInterface */ private $output; + /** @var IAppManager */ + private $appManager; /** * @param \OC\Repair $repair * @param IConfig $config */ - public function __construct(\OC\Repair $repair, IConfig $config, EventDispatcherInterface $dispatcher) { + public function __construct(\OC\Repair $repair, IConfig $config, EventDispatcherInterface $dispatcher, IAppManager $appManager) { $this->repair = $repair; $this->config = $config; $this->dispatcher = $dispatcher; + $this->appManager = $appManager; parent::__construct(); } @@ -66,39 +71,27 @@ protected function configure() { 'include-expensive', null, InputOption::VALUE_NONE, + 'Use this option when you want to include resource and load expensive tasks.') + ->addOption( + 'runtime', + null, + InputOption::VALUE_NONE, 'Use this option when you want to include resource and load expensive tasks.'); } protected function execute(InputInterface $input, OutputInterface $output) { - $includeExpensive = $input->getOption('include-expensive'); - if ($includeExpensive) { - foreach ($this->repair->getExpensiveRepairSteps() as $step) { - $this->repair->addStep($step); - } - } + $this->output = $output; - $apps = \OC::$server->getAppManager()->getInstalledApps(); - foreach ($apps as $app) { - if (!\OC_App::isEnabled($app)) { - continue; - } - $info = \OC_App::getAppInfo($app); - if (!\is_array($info)) { - continue; - } - \OC_App::loadApp($app); - $steps = $info['repair-steps']['post-migration']; - foreach ($steps as $step) { - try { - $this->repair->addStep($step); - } catch (Exception $ex) { - $output->writeln("Failed to load repair step for $app: {$ex->getMessage()}"); - } - } - } + $includeExpensive = $input->getOption('include-expensive'); + $runtime = $input->getOption('runtime'); + $this->buildRepairList($includeExpensive, $runtime, $this->repair); + // If not runtime mode, we need maintenance mode $maintenanceMode = $this->config->getSystemValue('maintenance', false); - $this->config->setSystemValue('maintenance', true); + if (!$runtime) { + $this->output->writeln('Enabling maintenance mode...'); + $this->config->setSystemValue('maintenance', true); + } $this->progress = new ProgressBar($output); $this->output = $output; @@ -112,7 +105,50 @@ protected function execute(InputInterface $input, OutputInterface $output) { $this->repair->run(); - $this->config->setSystemValue('maintenance', $maintenanceMode); + if (!$runtime) { + $this->output->writeln('Resetting maintenance mode...'); + $this->config->setSystemValue('maintenance', $maintenanceMode); + } + } + + public function buildRepairList($includeExpensive, $runtimeSteps, \OC\Repair $repair) { + $repair->clearSteps(); + $steps = []; + $steps = array_merge($steps, \OC\Repair::getRepairSteps()); + if ($includeExpensive) { + $steps = array_merge($steps, $repair->getExpensiveRepairSteps()); + } + $apps = $this->appManager->getInstalledApps(); + foreach ($apps as $app) { + if (!$this->appManager->isEnabledForUser($app)) { + continue; + } + $info = $this->appManager->getAppInfo($app); + if (!\is_array($info)) { + continue; + } + \OC_App::loadApp($app); + $appSteps = $info['repair-steps']['post-migration']; + foreach ($appSteps as $step) { + $steps[] = $step; + } + } + // Add to repair lib + foreach ($steps as $step) { + if (\is_string($step)) { + $step = \OC::$server->query($step); + } + // Skip step if runtime only and this one isnt + if ($runtimeSteps && !$step instanceof IRuntimeRepairStep) { + continue; + } + try { + $repair->addStep($step); + } catch (\Exception $e) { + $stepName = get_class($step); + $this->output->writeln("Problem loading repair step: $stepName"); + } + } } public function handleRepairFeedBack($event) { diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 5fe3b4aa2f8f..0032d8b9fd51 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -92,6 +92,13 @@ public function run() { } } + /** + * Clears the list of steps to run + */ + public function clearSteps() { + $this->repairSteps = []; + } + /** * Add repair step * diff --git a/lib/private/Repair/MoveAvatarOutsideHome.php b/lib/private/Repair/MoveAvatarOutsideHome.php index 8725f4a22510..963598f458e7 100644 --- a/lib/private/Repair/MoveAvatarOutsideHome.php +++ b/lib/private/Repair/MoveAvatarOutsideHome.php @@ -27,19 +27,19 @@ use OCP\ILogger; use OCP\IUserManager; use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; use OCP\IUser; use OC\Avatar; use OCP\IConfig; use OCP\IAvatarManager; use OCP\Files\NotFoundException; +use OCP\Migration\IRuntimeRepairStep; /** * Move avatars outside of their homes to the new location * * @package OC\Repair */ -class MoveAvatarOutsideHome implements IRepairStep { +class MoveAvatarOutsideHome implements IRuntimeRepairStep { /** @var \OCP\IConfig */ protected $config; diff --git a/lib/public/Migration/IRuntimeRepairStep.php b/lib/public/Migration/IRuntimeRepairStep.php new file mode 100644 index 000000000000..df0a66e62b28 --- /dev/null +++ b/lib/public/Migration/IRuntimeRepairStep.php @@ -0,0 +1,25 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ +namespace OCP\Migration; + +interface IRuntimeRepairStep extends IRepairStep { + +} \ No newline at end of file diff --git a/tests/lib/Command/Maintenance/RepairTest.php b/tests/lib/Command/Maintenance/RepairTest.php new file mode 100644 index 000000000000..1d7af7ce31a7 --- /dev/null +++ b/tests/lib/Command/Maintenance/RepairTest.php @@ -0,0 +1,80 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace Test\Command\Maintenance; + +use OC\Core\Command\Maintenance\Repair; +use OCP\App\IAppManager; +use OCP\IConfig; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Test\TestCase; + +/** + * Class RepairTest + * + * @package Test\Command\Maintenance + * @group DB + */ +class RepairTest extends TestCase { + + /** @var \OC\Repair */ + protected $repair; + /** @var Repair */ + protected $command; + /** @var IConfig */ + protected $config; + /** @var EventDispatcherInterface */ + protected $dispatcher; + /** @var IAppManager */ + protected $appManager; + + public function setUp() { + parent::setUp(); + $this->repair = $this->createMock(\OC\Repair::class); + $this->config = $this->createMock(IConfig::class); + $this->dispatcher = $this->createMock(EventDispatcherInterface::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->command = new Repair($this->repair, $this->config, $this->dispatcher, $this->appManager); + } + + /** + * @dataProvider buildStepsData + * @param $expensiveSteps + * @param $runtimeSteps + * @param $expensive + * @param $runtimeMode + * @param $expected + */ + public function testBuildSteps($expensiveSteps, $runtimeSteps, $expensive, $runtimeMode, $expectedCount) { + $this->appManager->method('getInstalledApps')->willReturn([]); + $this->repair->expects($this->exactly($expectedCount))->method('addStep'); + $this->command->buildRepairList($expensive, $runtimeMode, $this->repair); + //$this->assertSame($expected, $this->command->buildRepairList($expected, $runtimeMode, $this->repair)); + //$this->assertAttributeEquals($expected, '$repairSteps', ) + } + + public function buildStepsData() { + return [ + [[], [], false, false, []] + ]; + } + +}