Browse Source

Merge pull request #24760 from owncloud/objectstore_multibucket

Objectstore multibucket
Vincent Petry 8 years ago
parent
commit
e7110c7678

+ 63 - 2
lib/private/Files/Mount/ObjectHomeMountProvider.php

@@ -52,9 +52,27 @@ class ObjectHomeMountProvider implements IHomeMountProvider {
 	 * @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);
+	}
+
+	/**
+	 * @param IUser $user
+	 * @return array|null
+	 */
+	private function getSingleBucketObjectStoreConfig(IUser $user) {
 		$config = $this->config->getSystemValue('objectstore');
 		if (!is_array($config)) {
-			return null; //fall back to local home provider
+			return null;
 		}
 
 		// sanity checks
@@ -68,6 +86,49 @@ class ObjectHomeMountProvider implements IHomeMountProvider {
 		// instantiate object store implementation
 		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
 
-		return new MountPoint('\OC\Files\ObjectStore\HomeObjectStoreStorage', '/' . $user->getUID(), $config['arguments'], $loader);
+		return $config;
+	}
+
+	/**
+	 * @param IUser $user
+	 * @return array|null
+	 */
+	private function getMultiBucketObjectStoreConfig(IUser $user) {
+		$config = $this->config->getSystemValue('objectstore_multibucket');
+		if (!is_array($config)) {
+			return null;
+		}
+
+		// sanity checks
+		if (empty($config['class'])) {
+			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
+		}
+		if (!isset($config['arguments'])) {
+			$config['arguments'] = [];
+		}
+		$config['arguments']['user'] = $user;
+
+		$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);
+			$config['arguments']['bucket'] .= $mapper->getBucket();
+
+			$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']);
+
+		return $config;
 	}
 }

+ 52 - 0
lib/private/Files/ObjectStore/Mapper.php

@@ -0,0 +1,52 @@
+<?php
+/**
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ *
+ * @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\Files\ObjectStore;
+
+use OCP\IUser;
+
+/**
+ * Class Mapper
+ *
+ * @package OC\Files\ObjectStore
+ *
+ * Map a user to a bucket.
+ */
+class Mapper {
+	/** @var IUser */
+	private $user;
+
+	/**
+	 * Mapper constructor.
+	 *
+	 * @param IUser $user
+	 */
+	public function __construct(IUser $user) {
+		$this->user = $user;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getBucket() {
+		$hash = md5($this->user->getUID());
+		return substr($hash, 0, 3);
+	}
+}

+ 244 - 0
tests/lib/Files/Mount/ObjectHomeMountProviderTest.php

@@ -0,0 +1,244 @@
+<?php
+
+namespace Test\Files\Mount;
+
+use OC\Files\Mount\ObjectHomeMountProvider;
+use OCP\Files\Storage\IStorageFactory;
+use OCP\IConfig;
+use OCP\IUser;
+
+class ObjectHomeMountProviderTest extends \Test\TestCase {
+
+	/** @var ObjectHomeMountProvider */
+	protected $provider;
+
+	/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
+	protected $config;
+
+	/** @var IUser|\PHPUnit_Framework_MockObject_MockObject */
+	protected $user;
+
+	/** @var IStorageFactory|\PHPUnit_Framework_MockObject_MockObject */
+	protected $loader;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->config = $this->getMock('OCP\IConfig');
+		$this->user = $this->getMock('OCP\IUser');
+		$this->loader = $this->getMock('OCP\Files\Storage\IStorageFactory');
+
+		$this->provider = new ObjectHomeMountProvider($this->config);
+	}
+
+	public function testSingleBucket() {
+		$this->config->expects($this->once())
+			->method('getSystemValue')
+			->with($this->equalTo('objectstore'), '')
+			->willReturn([
+				'class' => 'Test\Files\Mount\FakeObjectStore',
+			]);
+
+		$this->user->expects($this->never())->method($this->anything());
+		$this->loader->expects($this->never())->method($this->anything());
+
+		$config = $this->invokePrivate($this->provider, 'getSingleBucketObjectStoreConfig', [$this->user, $this->loader]);
+
+		$this->assertArrayHasKey('class', $config);
+		$this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore');
+		$this->assertArrayHasKey('arguments', $config);
+		$this->assertArrayHasKey('user', $config['arguments']);
+		$this->assertSame($this->user, $config['arguments']['user']);
+		$this->assertArrayHasKey('objectstore', $config['arguments']);
+		$this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']);
+	}
+
+	public function testMultiBucket() {
+		$this->config->expects($this->once())
+			->method('getSystemValue')
+			->with($this->equalTo('objectstore_multibucket'), '')
+			->willReturn([
+				'class' => 'Test\Files\Mount\FakeObjectStore',
+			]);
+
+		$this->user->method('getUID')
+			->willReturn('uid');
+		$this->loader->expects($this->never())->method($this->anything());
+
+		$this->config->expects($this->once())
+			->method('getUserValue')
+			->with(
+				$this->equalTo('uid'),
+				$this->equalTo('homeobjectstore'),
+				$this->equalTo('bucket'),
+				$this->equalTo(null)
+			)->willReturn(null);
+
+		$this->config->expects($this->once())
+			->method('setUserValue')
+			->with(
+				$this->equalTo('uid'),
+				$this->equalTo('homeobjectstore'),
+				$this->equalTo('bucket'),
+				$this->equalTo('987'),
+				$this->equalTo(null)
+			);
+
+		$config = $this->invokePrivate($this->provider, 'getMultiBucketObjectStoreConfig', [$this->user, $this->loader]);
+
+		$this->assertArrayHasKey('class', $config);
+		$this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore');
+		$this->assertArrayHasKey('arguments', $config);
+		$this->assertArrayHasKey('user', $config['arguments']);
+		$this->assertSame($this->user, $config['arguments']['user']);
+		$this->assertArrayHasKey('objectstore', $config['arguments']);
+		$this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']);
+		$this->assertArrayHasKey('bucket', $config['arguments']);
+		$this->assertEquals('987', $config['arguments']['bucket']);
+	}
+
+	public function testMultiBucketWithPrefix() {
+		$this->config->expects($this->once())
+			->method('getSystemValue')
+			->with($this->equalTo('objectstore_multibucket'), '')
+			->willReturn([
+				'class' => 'Test\Files\Mount\FakeObjectStore',
+				'arguments' => [
+					'bucket' => 'myBucketPrefix',
+				],
+			]);
+
+		$this->user->method('getUID')
+			->willReturn('uid');
+		$this->loader->expects($this->never())->method($this->anything());
+
+		$this->config->expects($this->once())
+			->method('getUserValue')
+			->with(
+				$this->equalTo('uid'),
+				$this->equalTo('homeobjectstore'),
+				$this->equalTo('bucket'),
+				$this->equalTo(null)
+			)->willReturn(null);
+
+		$this->config->expects($this->once())
+			->method('setUserValue')
+			->with(
+				$this->equalTo('uid'),
+				$this->equalTo('homeobjectstore'),
+				$this->equalTo('bucket'),
+				$this->equalTo('myBucketPrefix987'),
+				$this->equalTo(null)
+			);
+
+		$config = $this->invokePrivate($this->provider, 'getMultiBucketObjectStoreConfig', [$this->user, $this->loader]);
+
+		$this->assertArrayHasKey('class', $config);
+		$this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore');
+		$this->assertArrayHasKey('arguments', $config);
+		$this->assertArrayHasKey('user', $config['arguments']);
+		$this->assertSame($this->user, $config['arguments']['user']);
+		$this->assertArrayHasKey('objectstore', $config['arguments']);
+		$this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']);
+		$this->assertArrayHasKey('bucket', $config['arguments']);
+		$this->assertEquals('myBucketPrefix987', $config['arguments']['bucket']);
+	}
+
+	public function testMultiBucketBucketAlreadySet() {
+		$this->config->expects($this->once())
+			->method('getSystemValue')
+			->with($this->equalTo('objectstore_multibucket'), '')
+			->willReturn([
+				'class' => 'Test\Files\Mount\FakeObjectStore',
+				'arguments' => [
+					'bucket' => 'myBucketPrefix',
+				],
+			]);
+
+		$this->user->method('getUID')
+			->willReturn('uid');
+		$this->loader->expects($this->never())->method($this->anything());
+
+		$this->config->expects($this->once())
+			->method('getUserValue')
+			->with(
+				$this->equalTo('uid'),
+				$this->equalTo('homeobjectstore'),
+				$this->equalTo('bucket'),
+				$this->equalTo(null)
+			)->willReturn('awesomeBucket1');
+
+		$this->config->expects($this->never())
+			->method('setUserValue');
+
+		$config = $this->invokePrivate($this->provider, 'getMultiBucketObjectStoreConfig', [$this->user, $this->loader]);
+
+		$this->assertArrayHasKey('class', $config);
+		$this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore');
+		$this->assertArrayHasKey('arguments', $config);
+		$this->assertArrayHasKey('user', $config['arguments']);
+		$this->assertSame($this->user, $config['arguments']['user']);
+		$this->assertArrayHasKey('objectstore', $config['arguments']);
+		$this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']);
+		$this->assertArrayHasKey('bucket', $config['arguments']);
+		$this->assertEquals('awesomeBucket1', $config['arguments']['bucket']);
+	}
+
+	public function testMultiBucketConfigFirst() {
+		$this->config->expects($this->once())
+			->method('getSystemValue')
+			->with($this->equalTo('objectstore_multibucket'))
+			->willReturn([
+				'class' => 'Test\Files\Mount\FakeObjectStore',
+			]);
+
+		$this->user->method('getUID')
+			->willReturn('uid');
+		$this->loader->expects($this->never())->method($this->anything());
+
+		$mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
+		$this->assertInstanceOf('OC\Files\Mount\MountPoint', $mount);
+	}
+
+	public function testMultiBucketConfigFirstFallBackSingle() {
+		$this->config->expects($this->at(0))
+			->method('getSystemValue')
+			->with($this->equalTo('objectstore_multibucket'))
+			->willReturn('');
+
+		$this->config->expects($this->at(1))
+			->method('getSystemValue')
+			->with($this->equalTo('objectstore'))
+			->willReturn([
+				'class' => 'Test\Files\Mount\FakeObjectStore',
+			]);
+
+		$this->user->method('getUID')
+			->willReturn('uid');
+		$this->loader->expects($this->never())->method($this->anything());
+		
+		$mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
+		$this->assertInstanceOf('OC\Files\Mount\MountPoint', $mount);
+	}
+
+	public function testNoObjectStore() {
+		$this->config->expects($this->exactly(2))
+			->method('getSystemValue')
+			->willReturn('');
+
+		$mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
+		$this->assertNull($mount);
+	}
+}
+
+class FakeObjectStore {
+	private $arguments;
+
+	public function __construct(array $arguments) {
+		$this->arguments = $arguments;
+	}
+
+	public function getArguments() {
+		return $this->arguments;
+	}
+}

+ 50 - 0
tests/lib/Files/ObjectStore/MapperTest.php

@@ -0,0 +1,50 @@
+<?php
+/**
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ *
+ * @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\Files\ObjectStore;
+
+
+use OC\Files\ObjectStore\Mapper;
+
+class MapperTest extends \Test\TestCase {
+
+	public function dataGetBucket() {
+		return [
+			['user', substr(md5('user'), 0, 3)],
+			['USER', substr(md5('USER'), 0, 3)],
+			['bc0e8b52-a66c-1035-90c6-d9663bda9e3f', substr(md5('bc0e8b52-a66c-1035-90c6-d9663bda9e3f'), 0, 3)],
+		];
+	}
+
+	/**
+	 * @dataProvider dataGetBucket
+	 * @param string $username
+	 * @param string $expectedBucket
+	 */
+	public function testGetBucket($username, $expectedBucket) {
+		$user = $this->getMock('OCP\IUser');
+		$user->method('getUID')
+			->willReturn($username);
+
+		$mapper = new Mapper($user);
+
+		$this->assertSame($expectedBucket, $mapper->getBucket());
+	}
+}