Skip to content

Commit

Permalink
move primary object store configuration to a single place
Browse files Browse the repository at this point in the history
Signed-off-by: Robin Appelman <robin@icewind.nl>
  • Loading branch information
icewind1991 committed Dec 1, 2022
1 parent 8434259 commit f4440c7
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 148 deletions.
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,7 @@
'OC\\Files\\ObjectStore\\Mapper' => $baseDir . '/lib/private/Files/ObjectStore/Mapper.php',
'OC\\Files\\ObjectStore\\NoopScanner' => $baseDir . '/lib/private/Files/ObjectStore/NoopScanner.php',
'OC\\Files\\ObjectStore\\ObjectStoreStorage' => $baseDir . '/lib/private/Files/ObjectStore/ObjectStoreStorage.php',
'OC\\Files\\ObjectStore\\PrimaryObjectStoreConfig' => $baseDir . '/lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php',
'OC\\Files\\ObjectStore\\S3' => $baseDir . '/lib/private/Files/ObjectStore/S3.php',
'OC\\Files\\ObjectStore\\S3ConnectionTrait' => $baseDir . '/lib/private/Files/ObjectStore/S3ConnectionTrait.php',
'OC\\Files\\ObjectStore\\S3ObjectTrait' => $baseDir . '/lib/private/Files/ObjectStore/S3ObjectTrait.php',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Files\\ObjectStore\\Mapper' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/Mapper.php',
'OC\\Files\\ObjectStore\\NoopScanner' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/NoopScanner.php',
'OC\\Files\\ObjectStore\\ObjectStoreStorage' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/ObjectStoreStorage.php',
'OC\\Files\\ObjectStore\\PrimaryObjectStoreConfig' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php',
'OC\\Files\\ObjectStore\\S3' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/S3.php',
'OC\\Files\\ObjectStore\\S3ConnectionTrait' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/S3ConnectionTrait.php',
'OC\\Files\\ObjectStore\\S3ObjectTrait' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/S3ObjectTrait.php',
Expand Down
109 changes: 16 additions & 93 deletions lib/private/Files/Mount/ObjectHomeMountProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,117 +24,40 @@
*/
namespace OC\Files\Mount;

use OC\Files\ObjectStore\HomeObjectStoreStorage;
use OC\Files\ObjectStore\PrimaryObjectStoreConfig;
use OCP\Files\Config\IHomeMountProvider;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorageFactory;
use OCP\IConfig;
use OCP\IUser;
use Psr\Log\LoggerInterface;

/**
* Mount provider for object store home storages
*/
class ObjectHomeMountProvider implements IHomeMountProvider {
/**
* @var IConfig
*/
private $config;
private PrimaryObjectStoreConfig $objectStoreConfig;

/**
* ObjectStoreHomeMountProvider constructor.
*
* @param IConfig $config
*/
public function __construct(IConfig $config) {
$this->config = $config;
public function __construct(PrimaryObjectStoreConfig $objectStoreConfig) {
$this->objectStoreConfig = $objectStoreConfig;
}

/**
* Get the cache mount for a user
* Get the home mount for a user
*
* @param IUser $user
* @param IStorageFactory $loader
* @return \OCP\Files\Mount\IMountPoint
*/
public function getHomeMountForUser(IUser $user, IStorageFactory $loader) {
$config = $this->getMultiBucketObjectStoreConfig($user);
if ($config === null) {
$config = $this->getSingleBucketObjectStoreConfig($user);
}

if ($config === null) {
return null;
}

return new MountPoint('\OC\Files\ObjectStore\HomeObjectStoreStorage', '/' . $user->getUID(), $config['arguments'], $loader, null, null, self::class);
}

/**
* @param IUser $user
* @return array|null
*/
private function getSingleBucketObjectStoreConfig(IUser $user) {
$config = $this->config->getSystemValue('objectstore');
if (!is_array($config)) {
return null;
}

// sanity checks
if (empty($config['class'])) {
\OC::$server->get(LoggerInterface::class)->error('No class given for objectstore', ['app' => 'files']);
}
if (!isset($config['arguments'])) {
$config['arguments'] = [];
}
// instantiate object store implementation
$config['arguments']['objectstore'] = new $config['class']($config['arguments']);

$config['arguments']['user'] = $user;

return $config;
}

/**
* @param IUser $user
* @return array|null
* @return ?IMountPoint
*/
private function getMultiBucketObjectStoreConfig(IUser $user) {
$config = $this->config->getSystemValue('objectstore_multibucket');
if (!is_array($config)) {
public function getHomeMountForUser(IUser $user, IStorageFactory $loader): ?IMountPoint {
$objectStore = $this->objectStoreConfig->getObjectStoreForUser($user);
if ($objectStore === null) {
return null;
}
$arguments = array_merge($this->objectStoreConfig->getObjectStoreArguments(), [
'objectstore' => $objectStore,
'user' => $user,
]);

// sanity checks
if (empty($config['class'])) {
\OC::$server->get(LoggerInterface::class)->error('No class given for objectstore', ['app' => 'files']);
}
if (!isset($config['arguments'])) {
$config['arguments'] = [];
}

$bucket = $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'bucket', null);

if ($bucket === null) {
/*
* Use any provided bucket argument as prefix
* and add the mapping from username => bucket
*/
if (!isset($config['arguments']['bucket'])) {
$config['arguments']['bucket'] = '';
}
$mapper = new \OC\Files\ObjectStore\Mapper($user, $this->config);
$numBuckets = isset($config['arguments']['num_buckets']) ? $config['arguments']['num_buckets'] : 64;
$config['arguments']['bucket'] .= $mapper->getBucket($numBuckets);

$this->config->setUserValue($user->getUID(), 'homeobjectstore', 'bucket', $config['arguments']['bucket']);
} else {
$config['arguments']['bucket'] = $bucket;
}

// instantiate object store implementation
$config['arguments']['objectstore'] = new $config['class']($config['arguments']);

$config['arguments']['user'] = $user;

return $config;
return new MountPoint(HomeObjectStoreStorage::class, '/' . $user->getUID(), $arguments, $loader, null, null, self::class);
}
}
61 changes: 12 additions & 49 deletions lib/private/Files/Mount/RootMountProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,79 +25,42 @@

use OC;
use OC\Files\ObjectStore\ObjectStoreStorage;
use OC\Files\ObjectStore\PrimaryObjectStoreConfig;
use OC\Files\Storage\LocalRootStorage;
use OC_App;
use OCP\Files\Config\IRootMountProvider;
use OCP\Files\ObjectStore\IObjectStore;
use OCP\Files\Storage\IStorageFactory;
use OCP\IConfig;
use Psr\Log\LoggerInterface;

class RootMountProvider implements IRootMountProvider {
private PrimaryObjectStoreConfig $objectStoreConfig;
private IConfig $config;
private LoggerInterface $logger;

public function __construct(IConfig $config, LoggerInterface $logger) {
public function __construct(PrimaryObjectStoreConfig $objectStoreConfig, IConfig $config) {
$this->objectStoreConfig = $objectStoreConfig;
$this->config = $config;
$this->logger = $logger;
}

public function getRootMounts(IStorageFactory $loader): array {
$objectStore = $this->config->getSystemValue('objectstore', null);
$objectStoreMultiBucket = $this->config->getSystemValue('objectstore_multibucket', null);
$objectStore = $this->objectStoreConfig->getObjectStoreForRoot();

if ($objectStoreMultiBucket) {
return [$this->getMultiBucketStoreRootMount($loader, $objectStoreMultiBucket)];
} elseif ($objectStore) {
if ($objectStore) {
return [$this->getObjectStoreRootMount($loader, $objectStore)];
} else {
return [$this->getLocalRootMount($loader)];
}
}

private function validateObjectStoreConfig(array &$config) {
if (empty($config['class'])) {
$this->logger->error('No class given for objectstore', ['app' => 'files']);
}
if (!isset($config['arguments'])) {
$config['arguments'] = [];
}

// instantiate object store implementation
$name = $config['class'];
if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
$segments = explode('\\', $name);
OC_App::loadApp(strtolower($segments[1]));
}
}

private function getLocalRootMount(IStorageFactory $loader): MountPoint {
$configDataDirectory = $this->config->getSystemValue("datadirectory", OC::$SERVERROOT . "/data");
return new MountPoint(LocalRootStorage::class, '/', ['datadir' => $configDataDirectory], $loader, null, null, self::class);
}

private function getObjectStoreRootMount(IStorageFactory $loader, array $config): MountPoint {
$this->validateObjectStoreConfig($config);

$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
// mount with plain / root object store implementation
$config['class'] = ObjectStoreStorage::class;

return new MountPoint($config['class'], '/', $config['arguments'], $loader, null, null, self::class);
}

private function getMultiBucketStoreRootMount(IStorageFactory $loader, array $config): MountPoint {
$this->validateObjectStoreConfig($config);

if (!isset($config['arguments']['bucket'])) {
$config['arguments']['bucket'] = '';
}
// put the root FS always in first bucket for multibucket configuration
$config['arguments']['bucket'] .= '0';

$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
// mount with plain / root object store implementation
$config['class'] = ObjectStoreStorage::class;
private function getObjectStoreRootMount(IStorageFactory $loader, IObjectStore $objectStore): MountPoint {
$arguments = array_merge($this->objectStoreConfig->getObjectStoreArguments(), [
'objectstore' => $objectStore,
]);

return new MountPoint($config['class'], '/', $config['arguments'], $loader, null, null, self::class);
return new MountPoint(ObjectStoreStorage::class, '/', $arguments, $loader, null, null, self::class);
}
}
135 changes: 135 additions & 0 deletions lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OC\Files\ObjectStore;

use OCP\Files\ObjectStore\IObjectStore;
use OCP\IConfig;
use OCP\IUser;
use Psr\Log\LoggerInterface;

class PrimaryObjectStoreConfig {
private IConfig $config;
private LoggerInterface $logger;

public function __construct(IConfig $config, LoggerInterface $logger) {
$this->config = $config;
$this->logger = $logger;
}

public function getObjectStoreForRoot(): ?IObjectStore {
$config = $this->getObjectStoreConfig();
if (!$config) {
return null;
}

if ($config['multibucket']) {
if (!isset($config['arguments']['bucket'])) {
$config['arguments']['bucket'] = '';
}

// put the root FS always in first bucket for multibucket configuration
$config['arguments']['bucket'] .= '0';
}

return new $config['class']($config['arguments']);
}

public function getObjectStoreForUser(IUser $user): ?IObjectStore {
$config = $this->getObjectStoreConfig();
if (!$config) {
return null;
}

if ($config['multibucket']) {
$config['arguments']['bucket'] = $this->getBucketForUser($user, $config);
}

// instantiate object store implementation
return new $config['class']($config['arguments']);
}

public function getObjectStoreArguments(): array {
$config = $this->getObjectStoreConfig();
if ($config === null) {
return [];
}
return $config['arguments'] ?? [];
}

private function getObjectStoreConfig(): ?array {
$objectStore = $this->config->getSystemValue('objectstore', null);
$objectStoreMultiBucket = $this->config->getSystemValue('objectstore_multibucket', null);

// new-style multibucket config uses the same 'objectstore' key but sets `'multibucket' => true`, transparently upgrade older style config
if ($objectStoreMultiBucket) {
$objectStoreMultiBucket['multibucket'] = true;
$objectStore = $objectStoreMultiBucket;
}
if ($objectStore === null) {
return null;
}
if (!isset($objectStore['multibucket'])) {
$objectStore['multibucket'] = false;
}
$this->validateObjectStoreConfig($objectStore);
return $objectStore;
}

private function validateObjectStoreConfig(array &$config) {
if (empty($config['class'])) {
$this->logger->error('No class given for objectstore', ['app' => 'files']);
}
if (!isset($config['arguments'])) {
$config['arguments'] = [];
}

// instantiate object store implementation
$name = $config['class'];
if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
$segments = explode('\\', $name);
\OC_App::loadApp(strtolower($segments[1]));
}
}

private function getBucketForUser(IUser $user, array $config): string {
$bucket = $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'bucket', null);

if ($bucket === null) {
/*
* Use any provided bucket argument as prefix
* and add the mapping from username => bucket
*/
if (!isset($config['arguments']['bucket'])) {
$config['arguments']['bucket'] = '';
}
$mapper = new Mapper($user, $this->config);
$numBuckets = isset($config['arguments']['num_buckets']) ? $config['arguments']['num_buckets'] : 64;
$bucket = $config['arguments']['bucket'] . $mapper->getBucket($numBuckets);

$this->config->setUserValue($user->getUID(), 'homeobjectstore', 'bucket', $bucket);
}

return $bucket;
}
}
6 changes: 4 additions & 2 deletions lib/private/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
use OC\Files\Node\HookConnector;
use OC\Files\Node\LazyRoot;
use OC\Files\Node\Root;
use OC\Files\ObjectStore\PrimaryObjectStoreConfig;
use OC\Files\SetupManager;
use OC\Files\Storage\StorageFactory;
use OC\Files\Template\TemplateManager;
Expand Down Expand Up @@ -969,10 +970,11 @@ public function __construct($webRoot, \OC\Config $config) {

$config = $c->get(\OCP\IConfig::class);
$logger = $c->get(LoggerInterface::class);
$objectStoreConfig = new PrimaryObjectStoreConfig($config, $c->get(LoggerInterface::class));
$manager->registerProvider(new CacheMountProvider($config));
$manager->registerHomeProvider(new LocalHomeMountProvider());
$manager->registerHomeProvider(new ObjectHomeMountProvider($config));
$manager->registerRootProvider(new RootMountProvider($config, $c->get(LoggerInterface::class)));
$manager->registerHomeProvider(new ObjectHomeMountProvider($objectStoreConfig));
$manager->registerRootProvider(new RootMountProvider($objectStoreConfig, $config));
$manager->registerRootProvider(new ObjectStorePreviewCacheMountProvider($logger, $config));

return $manager;
Expand Down
Loading

0 comments on commit f4440c7

Please sign in to comment.