aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Migration
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Migration')
-rw-r--r--lib/private/Migration/BackgroundRepair.php58
-rw-r--r--lib/private/Migration/ConsoleOutput.php22
-rw-r--r--lib/private/Migration/Exceptions/AttributeException.php17
-rw-r--r--lib/private/Migration/MetadataManager.php168
-rw-r--r--lib/private/Migration/NullOutput.php36
-rw-r--r--lib/private/Migration/SimpleOutput.php22
6 files changed, 240 insertions, 83 deletions
diff --git a/lib/private/Migration/BackgroundRepair.php b/lib/private/Migration/BackgroundRepair.php
index dd1b15c7492..d542b82d5e1 100644
--- a/lib/private/Migration/BackgroundRepair.php
+++ b/lib/private/Migration/BackgroundRepair.php
@@ -1,38 +1,17 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @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/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Migration;
-use OC\NeedsUpdateException;
use OC\Repair;
-use OC_App;
+use OCP\App\IAppManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\BackgroundJob\TimedJob;
-use OCP\EventDispatcher\IEventDispatcher;
use Psr\Log\LoggerInterface;
/**
@@ -42,10 +21,11 @@ use Psr\Log\LoggerInterface;
*/
class BackgroundRepair extends TimedJob {
public function __construct(
- private IEventDispatcher $dispatcher,
+ private Repair $repair,
ITimeFactory $time,
private LoggerInterface $logger,
private IJobList $jobList,
+ private IAppManager $appManager,
) {
parent::__construct($time);
$this->setInterval(15 * 60);
@@ -54,7 +34,6 @@ class BackgroundRepair extends TimedJob {
/**
* @param array $argument
* @throws \Exception
- * @throws \OC\NeedsUpdateException
*/
protected function run($argument): void {
if (!isset($argument['app']) || !isset($argument['step'])) {
@@ -64,18 +43,12 @@ class BackgroundRepair extends TimedJob {
}
$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;
- }
+ $this->appManager->loadApp($app);
$step = $argument['step'];
- $repair = new Repair([], $this->dispatcher, \OC::$server->get(LoggerInterface::class));
+ $this->repair->setRepairSteps([]);
try {
- $repair->addStep($step);
+ $this->repair->addStep($step);
} catch (\Exception $ex) {
$this->logger->error($ex->getMessage(), [
'app' => 'migration',
@@ -88,18 +61,9 @@ class BackgroundRepair extends TimedJob {
}
// execute the repair step
- $repair->run();
+ $this->repair->run();
// remove the job once executed successfully
$this->jobList->remove($this, $this->argument);
}
-
- /**
- * @codeCoverageIgnore
- * @param $app
- * @throws NeedsUpdateException
- */
- protected function loadApp($app): void {
- OC_App::loadApp($app);
- }
}
diff --git a/lib/private/Migration/ConsoleOutput.php b/lib/private/Migration/ConsoleOutput.php
index 841e3d302fc..31412bf4ff0 100644
--- a/lib/private/Migration/ConsoleOutput.php
+++ b/lib/private/Migration/ConsoleOutput.php
@@ -1,23 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2017, ownCloud GmbH
- *
- * @author Morris Jobke <hey@morrisjobke.de>
- *
- * @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/>
- *
+ * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2015 ownCloud GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Migration;
diff --git a/lib/private/Migration/Exceptions/AttributeException.php b/lib/private/Migration/Exceptions/AttributeException.php
new file mode 100644
index 00000000000..3daf99032ad
--- /dev/null
+++ b/lib/private/Migration/Exceptions/AttributeException.php
@@ -0,0 +1,17 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OC\Migration\Exceptions;
+
+use Exception;
+
+/**
+ * @since 30.0.0
+ */
+class AttributeException extends Exception {
+}
diff --git a/lib/private/Migration/MetadataManager.php b/lib/private/Migration/MetadataManager.php
new file mode 100644
index 00000000000..f4cb95342b4
--- /dev/null
+++ b/lib/private/Migration/MetadataManager.php
@@ -0,0 +1,168 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OC\Migration;
+
+use OC\DB\Connection;
+use OC\DB\MigrationService;
+use OC\Migration\Exceptions\AttributeException;
+use OCP\App\IAppManager;
+use OCP\Migration\Attributes\GenericMigrationAttribute;
+use OCP\Migration\Attributes\MigrationAttribute;
+use Psr\Log\LoggerInterface;
+use ReflectionClass;
+
+/**
+ * Helps managing DB Migrations' Metadata
+ *
+ * @since 30.0.0
+ */
+class MetadataManager {
+ public function __construct(
+ private readonly IAppManager $appManager,
+ private readonly Connection $connection,
+ private readonly LoggerInterface $logger,
+ ) {
+ }
+
+ /**
+ * We get all migrations from an app (or 'core'), and
+ * for each migration files we extract its attributes
+ *
+ * @param string $appId
+ *
+ * @return array<string, MigrationAttribute[]>
+ * @since 30.0.0
+ */
+ public function extractMigrationAttributes(string $appId): array {
+ $ms = new MigrationService($appId, $this->connection);
+
+ $metadata = [];
+ foreach ($ms->getAvailableVersions() as $version) {
+ $metadata[$version] = [];
+ $class = new ReflectionClass($ms->createInstance($version));
+ $attributes = $class->getAttributes();
+ foreach ($attributes as $attribute) {
+ $item = $attribute->newInstance();
+ if ($item instanceof MigrationAttribute) {
+ $metadata[$version][] = $item;
+ }
+ }
+ }
+
+ return $metadata;
+ }
+
+ /**
+ * convert direct data from release metadata into a list of Migrations' Attribute
+ *
+ * @param array<array-key, array<array-key, array>> $metadata
+ * @param bool $filterKnownMigrations ignore metadata already done in local instance
+ *
+ * @return array{apps: array<array-key, array<string, MigrationAttribute[]>>, core: array<string, MigrationAttribute[]>}
+ * @since 30.0.0
+ */
+ public function getMigrationsAttributesFromReleaseMetadata(
+ array $metadata,
+ bool $filterKnownMigrations = false,
+ ): array {
+ $appsAttributes = [];
+ foreach (array_keys($metadata['apps']) as $appId) {
+ if ($filterKnownMigrations && !$this->appManager->isEnabledForAnyone($appId)) {
+ continue; // if not interested and app is not installed
+ }
+
+ $done = ($filterKnownMigrations) ? $this->getKnownMigrations($appId) : [];
+ $appsAttributes[$appId] = $this->parseMigrations($metadata['apps'][$appId] ?? [], $done);
+ }
+
+ $done = ($filterKnownMigrations) ? $this->getKnownMigrations('core') : [];
+ return [
+ 'core' => $this->parseMigrations($metadata['core'] ?? [], $done),
+ 'apps' => $appsAttributes
+ ];
+ }
+
+ /**
+ * returns list of installed apps that does not support migrations metadata (yet)
+ *
+ * @param array<array-key, array<array-key, array>> $metadata
+ *
+ * @return string[]
+ * @since 30.0.0
+ */
+ public function getUnsupportedApps(array $metadata): array {
+ return array_values(array_diff($this->appManager->getEnabledApps(), array_keys($metadata['apps'])));
+ }
+
+ /**
+ * convert raw data to a list of MigrationAttribute
+ *
+ * @param array $migrations
+ * @param array $ignoreMigrations
+ *
+ * @return array<string, MigrationAttribute[]>
+ */
+ private function parseMigrations(array $migrations, array $ignoreMigrations = []): array {
+ $parsed = [];
+ foreach (array_keys($migrations) as $entry) {
+ if (in_array($entry, $ignoreMigrations)) {
+ continue;
+ }
+
+ $parsed[$entry] = [];
+ foreach ($migrations[$entry] as $item) {
+ try {
+ $parsed[$entry][] = $this->createAttribute($item);
+ } catch (AttributeException $e) {
+ $this->logger->warning('exception while trying to create attribute', ['exception' => $e, 'item' => json_encode($item)]);
+ $parsed[$entry][] = new GenericMigrationAttribute($item);
+ }
+ }
+ }
+
+ return $parsed;
+ }
+
+ /**
+ * returns migrations already done
+ *
+ * @param string $appId
+ *
+ * @return array
+ * @throws \Exception
+ */
+ private function getKnownMigrations(string $appId): array {
+ $ms = new MigrationService($appId, $this->connection);
+ return $ms->getMigratedVersions();
+ }
+
+ /**
+ * generate (deserialize) a MigrationAttribute from a serialized version
+ *
+ * @param array $item
+ *
+ * @return MigrationAttribute
+ * @throws AttributeException
+ */
+ private function createAttribute(array $item): MigrationAttribute {
+ $class = $item['class'] ?? '';
+ $namespace = 'OCP\Migration\Attributes\\';
+ if (!str_starts_with($class, $namespace)
+ || !ctype_alpha(substr($class, strlen($namespace)))) {
+ throw new AttributeException('class name does not looks valid');
+ }
+
+ try {
+ $attribute = new $class($item['table'] ?? '');
+ return $attribute->import($item);
+ } catch (\Error) {
+ throw new AttributeException('cannot import Attribute');
+ }
+ }
+}
diff --git a/lib/private/Migration/NullOutput.php b/lib/private/Migration/NullOutput.php
new file mode 100644
index 00000000000..8db7b950af8
--- /dev/null
+++ b/lib/private/Migration/NullOutput.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OC\Migration;
+
+use OCP\Migration\IOutput;
+
+/**
+ * Class NullOutput
+ *
+ * A simple IOutput that discards all output
+ *
+ * @package OC\Migration
+ */
+class NullOutput implements IOutput {
+ public function debug(string $message): void {
+ }
+
+ public function info($message): void {
+ }
+
+ public function warning($message): void {
+ }
+
+ public function startProgress($max = 0): void {
+ }
+
+ public function advance($step = 1, $description = ''): void {
+ }
+
+ public function finishProgress(): void {
+ }
+}
diff --git a/lib/private/Migration/SimpleOutput.php b/lib/private/Migration/SimpleOutput.php
index f1b06d008bb..b7a07cc6ff2 100644
--- a/lib/private/Migration/SimpleOutput.php
+++ b/lib/private/Migration/SimpleOutput.php
@@ -1,23 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2017, ownCloud GmbH
- *
- * @author Joas Schilling <coding@schilljs.com>
- *
- * @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/>
- *
+ * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2015 ownCloud GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Migration;