Merge pull request #24274 from owncloud/async-repair-steps
Introduce background repair steps
This commit is contained in:
commit
0e774e0fd8
|
@ -80,6 +80,9 @@ class InfoParser {
|
|||
if (!array_key_exists('post-migration', $array['repair-steps'])) {
|
||||
$array['repair-steps']['post-migration'] = [];
|
||||
}
|
||||
if (!array_key_exists('live-migration', $array['repair-steps'])) {
|
||||
$array['repair-steps']['live-migration'] = [];
|
||||
}
|
||||
|
||||
if (array_key_exists('documentation', $array) && is_array($array['documentation'])) {
|
||||
foreach ($array['documentation'] as $key => $url) {
|
||||
|
@ -110,6 +113,9 @@ class InfoParser {
|
|||
if (isset($array['repair-steps']['post-migration']['step']) && is_array($array['repair-steps']['post-migration']['step'])) {
|
||||
$array['repair-steps']['post-migration'] = $array['repair-steps']['post-migration']['step'];
|
||||
}
|
||||
if (isset($array['repair-steps']['live-migration']['step']) && is_array($array['repair-steps']['live-migration']['step'])) {
|
||||
$array['repair-steps']['live-migration'] = $array['repair-steps']['live-migration']['step'];
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
namespace OC\Migration;
|
||||
|
||||
use OC\BackgroundJob\JobList;
|
||||
use OC\BackgroundJob\TimedJob;
|
||||
use OC\NeedsUpdateException;
|
||||
use OC\Repair;
|
||||
use OC_App;
|
||||
use OCP\BackgroundJob\IJobList;
|
||||
use OCP\ILogger;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Class BackgroundRepair
|
||||
*
|
||||
* @package OC\Migration
|
||||
*/
|
||||
class BackgroundRepair extends TimedJob {
|
||||
|
||||
/** @var IJobList */
|
||||
private $jobList;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/** @var EventDispatcher */
|
||||
private $dispatcher;
|
||||
|
||||
public function setDispatcher(EventDispatcher $dispatcher) {
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
/**
|
||||
* run the job, then remove it from the job list
|
||||
*
|
||||
* @param JobList $jobList
|
||||
* @param ILogger $logger
|
||||
*/
|
||||
public function execute($jobList, ILogger $logger = null) {
|
||||
// add an interval of 15 mins
|
||||
$this->setInterval(15*60);
|
||||
|
||||
$this->jobList = $jobList;
|
||||
$this->logger = $logger;
|
||||
parent::execute($jobList, $logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $argument
|
||||
* @throws \Exception
|
||||
* @throws \OC\NeedsUpdateException
|
||||
*/
|
||||
protected function run($argument) {
|
||||
if (!isset($argument['app']) || !isset($argument['step'])) {
|
||||
// remove the job - we can never execute it
|
||||
$this->jobList->remove($this, $this->argument);
|
||||
return;
|
||||
}
|
||||
$app = $argument['app'];
|
||||
|
||||
try {
|
||||
$this->loadApp($app);
|
||||
} catch (NeedsUpdateException $ex) {
|
||||
// as long as the app is not yet done with it's offline migration
|
||||
// we better not start with the live migration
|
||||
return;
|
||||
}
|
||||
|
||||
$step = $argument['step'];
|
||||
$repair = new Repair([], $this->dispatcher);
|
||||
try {
|
||||
$repair->addStep($step);
|
||||
} catch (\Exception $ex) {
|
||||
$this->logger->logException($ex,[
|
||||
'app' => 'migration'
|
||||
]);
|
||||
|
||||
// remove the job - we can never execute it
|
||||
$this->jobList->remove($this, $this->argument);
|
||||
return;
|
||||
}
|
||||
|
||||
// execute the repair step
|
||||
$repair->run();
|
||||
|
||||
// remove the job once executed successfully
|
||||
$this->jobList->remove($this, $this->argument);
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* @param $app
|
||||
* @throws NeedsUpdateException
|
||||
*/
|
||||
protected function loadApp($app) {
|
||||
OC_App::loadApp($app);
|
||||
}
|
||||
}
|
|
@ -1153,6 +1153,7 @@ class OC_App {
|
|||
OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
|
||||
}
|
||||
self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
|
||||
self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
|
||||
unset(self::$appVersion[$appId]);
|
||||
// run upgrade code
|
||||
if (file_exists($appPath . '/appinfo/update.php')) {
|
||||
|
@ -1209,6 +1210,19 @@ class OC_App {
|
|||
$r->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $appId
|
||||
* @param string[] $steps
|
||||
*/
|
||||
private static function setupLiveMigrations($appId, array $steps) {
|
||||
$queue = \OC::$server->getJobList();
|
||||
foreach ($steps as $step) {
|
||||
$queue->add('OC\Migration\BackgroundRepair', [
|
||||
'app' => $appId,
|
||||
'step' => $step]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $appId
|
||||
* @return \OC\Files\View|false
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
},
|
||||
"repair-steps": {
|
||||
"pre-migration": [],
|
||||
"post-migration": []
|
||||
"post-migration": [],
|
||||
"live-migration": []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\Migration;
|
||||
|
||||
|
||||
use OC\Migration\BackgroundRepair;
|
||||
use OC\NeedsUpdateException;
|
||||
use OCP\ILogger;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\IRepairStep;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\GenericEvent;
|
||||
use Test\TestCase;
|
||||
|
||||
class TestRepairStep implements IRepairStep {
|
||||
|
||||
/**
|
||||
* Returns the step's name
|
||||
*
|
||||
* @return string
|
||||
* @since 9.1.0
|
||||
*/
|
||||
public function getName() {
|
||||
return 'A test repair step';
|
||||
}
|
||||
|
||||
/**
|
||||
* Run repair step.
|
||||
* Must throw exception on error.
|
||||
*
|
||||
* @since 9.1.0
|
||||
* @throws \Exception in case of failure
|
||||
*/
|
||||
public function run(IOutput $output) {
|
||||
// TODO: Implement run() method.
|
||||
}
|
||||
}
|
||||
|
||||
class BackgroundRepairTest extends TestCase {
|
||||
|
||||
/** @var \OC\BackgroundJob\JobList | \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $jobList;
|
||||
|
||||
/** @var BackgroundRepair | \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $job;
|
||||
|
||||
/** @var ILogger | \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $logger;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->jobList = $this->getMockBuilder('OC\BackgroundJob\JobList')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->logger = $this->getMockBuilder('OCP\ILogger')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->job = $this->getMock('OC\Migration\BackgroundRepair', ['loadApp']);
|
||||
}
|
||||
|
||||
public function testNoArguments() {
|
||||
$this->jobList->expects($this->once())->method('remove');
|
||||
$this->job->execute($this->jobList);
|
||||
}
|
||||
|
||||
public function testAppUpgrading() {
|
||||
$this->jobList->expects($this->never())->method('remove');
|
||||
$this->job->expects($this->once())->method('loadApp')->with('test')->willThrowException(new NeedsUpdateException());
|
||||
$this->job->setArgument([
|
||||
'app' => 'test',
|
||||
'step' => 'j'
|
||||
]);
|
||||
$this->job->execute($this->jobList);
|
||||
}
|
||||
|
||||
public function testUnknownStep() {
|
||||
$this->jobList->expects($this->once())->method('remove');
|
||||
$this->logger->expects($this->once())->method('logException');
|
||||
$this->job->setArgument([
|
||||
'app' => 'test',
|
||||
'step' => 'j'
|
||||
]);
|
||||
$this->job->execute($this->jobList, $this->logger);
|
||||
}
|
||||
|
||||
public function testWorkingStep() {
|
||||
/** @var EventDispatcher | \PHPUnit_Framework_MockObject_MockObject $dispatcher */
|
||||
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcher', []);
|
||||
$dispatcher->expects($this->once())->method('dispatch')
|
||||
->with('\OC\Repair::step', new GenericEvent('\OC\Repair::step', ['A test repair step']));
|
||||
|
||||
$this->jobList->expects($this->once())->method('remove');
|
||||
$this->job->setDispatcher($dispatcher);
|
||||
$this->job->setArgument([
|
||||
'app' => 'test',
|
||||
'step' => '\Test\Migration\TestRepairStep'
|
||||
]);
|
||||
$this->job->execute($this->jobList, $this->logger);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue