Merge pull request #13641 from owncloud/cache-storage-status
Store storage availability in database
This commit is contained in:
commit
b3a1aef934
|
@ -496,8 +496,16 @@ class OC_Mount_Config {
|
||||||
if (class_exists($class)) {
|
if (class_exists($class)) {
|
||||||
try {
|
try {
|
||||||
$storage = new $class($options);
|
$storage = new $class($options);
|
||||||
if ($storage->test($isPersonal)) {
|
|
||||||
return self::STATUS_SUCCESS;
|
try {
|
||||||
|
$result = $storage->test($isPersonal);
|
||||||
|
$storage->setAvailability($result);
|
||||||
|
if ($result) {
|
||||||
|
return self::STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$storage->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
\OCP\Util::logException('files_external', $exception);
|
\OCP\Util::logException('files_external', $exception);
|
||||||
|
|
|
@ -102,6 +102,18 @@
|
||||||
<length>4</length>
|
<length>4</length>
|
||||||
</field>
|
</field>
|
||||||
|
|
||||||
|
<field>
|
||||||
|
<name>available</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<default>true</default>
|
||||||
|
<notnull>true</notnull>
|
||||||
|
</field>
|
||||||
|
|
||||||
|
<field>
|
||||||
|
<name>last_checked</name>
|
||||||
|
<type>integer</type>
|
||||||
|
</field>
|
||||||
|
|
||||||
<index>
|
<index>
|
||||||
<name>storages_id_index</name>
|
<name>storages_id_index</name>
|
||||||
<unique>true</unique>
|
<unique>true</unique>
|
||||||
|
|
|
@ -43,9 +43,10 @@ class Storage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \OC\Files\Storage\Storage|string $storage
|
* @param \OC\Files\Storage\Storage|string $storage
|
||||||
|
* @param bool $isAvailable
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
*/
|
*/
|
||||||
public function __construct($storage) {
|
public function __construct($storage, $isAvailable = true) {
|
||||||
if ($storage instanceof \OC\Files\Storage\Storage) {
|
if ($storage instanceof \OC\Files\Storage\Storage) {
|
||||||
$this->storageId = $storage->getId();
|
$this->storageId = $storage->getId();
|
||||||
} else {
|
} else {
|
||||||
|
@ -53,17 +54,14 @@ class Storage {
|
||||||
}
|
}
|
||||||
$this->storageId = self::adjustStorageId($this->storageId);
|
$this->storageId = self::adjustStorageId($this->storageId);
|
||||||
|
|
||||||
$sql = 'SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?';
|
if ($row = self::getStorageById($this->storageId)) {
|
||||||
$result = \OC_DB::executeAudited($sql, array($this->storageId));
|
|
||||||
if ($row = $result->fetchRow()) {
|
|
||||||
$this->numericId = $row['numeric_id'];
|
$this->numericId = $row['numeric_id'];
|
||||||
} else {
|
} else {
|
||||||
$connection = \OC_DB::getConnection();
|
$connection = \OC_DB::getConnection();
|
||||||
if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId])) {
|
if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId, 'available' => $isAvailable])) {
|
||||||
$this->numericId = \OC_DB::insertid('*PREFIX*storages');
|
$this->numericId = \OC_DB::insertid('*PREFIX*storages');
|
||||||
} else {
|
} else {
|
||||||
$result = \OC_DB::executeAudited($sql, array($this->storageId));
|
if ($row = self::getStorageById($this->storageId)) {
|
||||||
if ($row = $result->fetchRow()) {
|
|
||||||
$this->numericId = $row['numeric_id'];
|
$this->numericId = $row['numeric_id'];
|
||||||
} else {
|
} else {
|
||||||
throw new \RuntimeException('Storage could neither be inserted nor be selected from the database');
|
throw new \RuntimeException('Storage could neither be inserted nor be selected from the database');
|
||||||
|
@ -72,6 +70,16 @@ class Storage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $storageId
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public static function getStorageById($storageId) {
|
||||||
|
$sql = 'SELECT * FROM `*PREFIX*storages` WHERE `id` = ?';
|
||||||
|
$result = \OC_DB::executeAudited($sql, array($storageId));
|
||||||
|
return $result->fetchRow();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjusts the storage id to use md5 if too long
|
* Adjusts the storage id to use md5 if too long
|
||||||
* @param string $storageId storage id
|
* @param string $storageId storage id
|
||||||
|
@ -120,15 +128,35 @@ class Storage {
|
||||||
public static function getNumericStorageId($storageId) {
|
public static function getNumericStorageId($storageId) {
|
||||||
$storageId = self::adjustStorageId($storageId);
|
$storageId = self::adjustStorageId($storageId);
|
||||||
|
|
||||||
$sql = 'SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?';
|
if ($row = self::getStorageById($storageId)) {
|
||||||
$result = \OC_DB::executeAudited($sql, array($storageId));
|
|
||||||
if ($row = $result->fetchRow()) {
|
|
||||||
return $row['numeric_id'];
|
return $row['numeric_id'];
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array|null [ available, last_checked ]
|
||||||
|
*/
|
||||||
|
public function getAvailability() {
|
||||||
|
if ($row = self::getStorageById($this->storageId)) {
|
||||||
|
return [
|
||||||
|
'available' => $row['available'],
|
||||||
|
'last_checked' => $row['last_checked']
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $isAvailable
|
||||||
|
*/
|
||||||
|
public function setAvailability($isAvailable) {
|
||||||
|
$sql = 'UPDATE `*PREFIX*storages` SET `available` = ?, `last_checked` = ? WHERE `id` = ?';
|
||||||
|
\OC_DB::executeAudited($sql, array($isAvailable, time(), $this->storageId));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a string storage id is known
|
* Check if a string storage id is known
|
||||||
*
|
*
|
||||||
|
|
|
@ -29,6 +29,7 @@ namespace OC\Files\Mount;
|
||||||
use \OC\Files\Filesystem;
|
use \OC\Files\Filesystem;
|
||||||
use OC\Files\Storage\StorageFactory;
|
use OC\Files\Storage\StorageFactory;
|
||||||
use OC\Files\Storage\Storage;
|
use OC\Files\Storage\Storage;
|
||||||
|
use OC\Files\Storage\Wrapper\Wrapper;
|
||||||
use OCP\Files\Mount\IMountPoint;
|
use OCP\Files\Mount\IMountPoint;
|
||||||
|
|
||||||
class MountPoint implements IMountPoint {
|
class MountPoint implements IMountPoint {
|
||||||
|
@ -92,7 +93,11 @@ class MountPoint implements IMountPoint {
|
||||||
$this->mountPoint = $mountpoint;
|
$this->mountPoint = $mountpoint;
|
||||||
if ($storage instanceof Storage) {
|
if ($storage instanceof Storage) {
|
||||||
$this->class = get_class($storage);
|
$this->class = get_class($storage);
|
||||||
$this->storage = $this->loader->wrap($this, $storage);
|
$this->storage = $storage;
|
||||||
|
// only wrap if not already wrapped
|
||||||
|
if (!($this->storage instanceof Wrapper)) {
|
||||||
|
$this->storage = $this->loader->wrap($this, $this->storage);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Update old classes to new namespace
|
// Update old classes to new namespace
|
||||||
if (strpos($storage, 'OC_Filestorage_') !== false) {
|
if (strpos($storage, 'OC_Filestorage_') !== false) {
|
||||||
|
|
|
@ -404,6 +404,11 @@ abstract class Common implements Storage {
|
||||||
return implode('/', $output);
|
return implode('/', $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test a storage for availability
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
public function test() {
|
public function test() {
|
||||||
if ($this->stat('')) {
|
if ($this->stat('')) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -650,4 +655,18 @@ abstract class Common implements Storage {
|
||||||
public function changeLock($path, $type, ILockingProvider $provider) {
|
public function changeLock($path, $type, ILockingProvider $provider) {
|
||||||
$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
|
$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array [ available, last_checked ]
|
||||||
|
*/
|
||||||
|
public function getAvailability() {
|
||||||
|
return $this->getStorageCache()->getAvailability();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $isAvailable
|
||||||
|
*/
|
||||||
|
public function setAvailability($isAvailable) {
|
||||||
|
$this->getStorageCache()->setAvailability($isAvailable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,462 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2015, 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\Storage\Wrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Availability checker for storages
|
||||||
|
*
|
||||||
|
* Throws a StorageNotAvailableException for storages with known failures
|
||||||
|
*/
|
||||||
|
class Availability extends Wrapper {
|
||||||
|
const RECHECK_TTL_SEC = 600; // 10 minutes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function updateAvailability() {
|
||||||
|
try {
|
||||||
|
$result = $this->test();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$result = false;
|
||||||
|
}
|
||||||
|
$this->setAvailability($result);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function isAvailable() {
|
||||||
|
$availability = $this->getAvailability();
|
||||||
|
if (!$availability['available']) {
|
||||||
|
// trigger a recheck if TTL reached
|
||||||
|
if ((time() - $availability['last_checked']) > self::RECHECK_TTL_SEC) {
|
||||||
|
return $this->updateAvailability();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $availability['available'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \OCP\Files\StorageNotAvailableException
|
||||||
|
*/
|
||||||
|
private function checkAvailability() {
|
||||||
|
if (!$this->isAvailable()) {
|
||||||
|
throw new \OCP\Files\StorageNotAvailableException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function mkdir($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::mkdir($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function rmdir($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::rmdir($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function opendir($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::opendir($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function is_dir($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::is_dir($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function is_file($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::is_file($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function stat($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::stat($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function filetype($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::filetype($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function filesize($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::filesize($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function isCreatable($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::isCreatable($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function isReadable($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::isReadable($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function isUpdatable($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::isUpdatable($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function isDeletable($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::isDeletable($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function isSharable($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::isSharable($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function getPermissions($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::getPermissions($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function file_exists($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::file_exists($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function filemtime($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::filemtime($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function file_get_contents($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::file_get_contents($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function file_put_contents($path, $data) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::file_put_contents($path, $data);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function unlink($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::unlink($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function rename($path1, $path2) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::rename($path1, $path2);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function copy($path1, $path2) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::copy($path1, $path2);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function fopen($path, $mode) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::fopen($path, $mode);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function getMimeType($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::getMimeType($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function hash($type, $path, $raw = false) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::hash($type, $path, $raw);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function free_space($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::free_space($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function search($query) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::search($query);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function touch($path, $mtime = null) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::touch($path, $mtime);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function getLocalFile($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::getLocalFile($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function getLocalFolder($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::getLocalFolder($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function hasUpdated($path, $time) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::hasUpdated($path, $time);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function getOwner($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::getOwner($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function getETag($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::getETag($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function getDirectDownload($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::getDirectDownload($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function getMetaData($path) {
|
||||||
|
$this->checkAvailability();
|
||||||
|
try {
|
||||||
|
return parent::getMetaData($path);
|
||||||
|
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||||
|
$this->setAvailability(false);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -497,6 +497,24 @@ class Wrapper implements \OC\Files\Storage\Storage {
|
||||||
return $this->storage->getDirectDownload($path);
|
return $this->storage->getDirectDownload($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get availability of the storage
|
||||||
|
*
|
||||||
|
* @return array [ available, last_checked ]
|
||||||
|
*/
|
||||||
|
public function getAvailability() {
|
||||||
|
return $this->storage->getAvailability();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set availability of the storage
|
||||||
|
*
|
||||||
|
* @param bool $isAvailable
|
||||||
|
*/
|
||||||
|
public function setAvailability($isAvailable) {
|
||||||
|
$this->storage->setAvailability($isAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $path the path of the target folder
|
* @param string $path the path of the target folder
|
||||||
* @param string $fileName the name of the file itself
|
* @param string $fileName the name of the file itself
|
||||||
|
|
|
@ -143,6 +143,14 @@ class OC_Util {
|
||||||
return $storage;
|
return $storage;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// install storage availability wrapper, before most other wrappers
|
||||||
|
\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
|
||||||
|
if (!$storage->isLocal()) {
|
||||||
|
return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
|
||||||
|
}
|
||||||
|
return $storage;
|
||||||
|
});
|
||||||
|
|
||||||
\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
|
\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
|
||||||
// set up quota for home storages, even for other users
|
// set up quota for home storages, even for other users
|
||||||
// which can happen when using sharing
|
// which can happen when using sharing
|
||||||
|
|
|
@ -439,4 +439,24 @@ interface Storage {
|
||||||
* @since 8.1.0
|
* @since 8.1.0
|
||||||
*/
|
*/
|
||||||
public function changeLock($path, $type, ILockingProvider $provider);
|
public function changeLock($path, $type, ILockingProvider $provider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test a storage for availability
|
||||||
|
*
|
||||||
|
* @since 8.2.0
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function test();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 8.2.0
|
||||||
|
* @return array [ available, last_checked ]
|
||||||
|
*/
|
||||||
|
public function getAvailability();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 8.2.0
|
||||||
|
* @param bool $isAvailable
|
||||||
|
*/
|
||||||
|
public function setAvailability($isAvailable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,4 +70,25 @@ class MountPoint extends \Test\TestCase {
|
||||||
// storage wrapper never called
|
// storage wrapper never called
|
||||||
$this->assertFalse($called);
|
$this->assertFalse($called);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testWrappedStorage() {
|
||||||
|
$storage = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Wrapper')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$loader = $this->getMock('\OCP\Files\Storage\IStorageFactory');
|
||||||
|
$loader->expects($this->never())
|
||||||
|
->method('getInstance');
|
||||||
|
$loader->expects($this->never())
|
||||||
|
->method('wrap');
|
||||||
|
|
||||||
|
$mountPoint = new \OC\Files\Mount\MountPoint(
|
||||||
|
$storage,
|
||||||
|
'/mountpoint',
|
||||||
|
null,
|
||||||
|
$loader
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals($storage, $mountPoint->getStorage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2015, 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\Storage\Wrapper;
|
||||||
|
|
||||||
|
class Availability extends \Test\TestCase {
|
||||||
|
protected function getWrapperInstance() {
|
||||||
|
$storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$wrapper = new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
|
||||||
|
return [$storage, $wrapper];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage is available
|
||||||
|
*/
|
||||||
|
public function testAvailable() {
|
||||||
|
list($storage, $wrapper) = $this->getWrapperInstance();
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('getAvailability')
|
||||||
|
->willReturn(['available' => true, 'last_checked' => 0]);
|
||||||
|
$storage->expects($this->never())
|
||||||
|
->method('test');
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('mkdir');
|
||||||
|
|
||||||
|
$wrapper->mkdir('foobar');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage marked unavailable, TTL not expired
|
||||||
|
*
|
||||||
|
* @expectedException \OCP\Files\StorageNotAvailableException
|
||||||
|
*/
|
||||||
|
public function testUnavailable() {
|
||||||
|
list($storage, $wrapper) = $this->getWrapperInstance();
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('getAvailability')
|
||||||
|
->willReturn(['available' => false, 'last_checked' => time()]);
|
||||||
|
$storage->expects($this->never())
|
||||||
|
->method('test');
|
||||||
|
$storage->expects($this->never())
|
||||||
|
->method('mkdir');
|
||||||
|
|
||||||
|
$wrapper->mkdir('foobar');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage marked unavailable, TTL expired
|
||||||
|
*/
|
||||||
|
public function testUnavailableRecheck() {
|
||||||
|
list($storage, $wrapper) = $this->getWrapperInstance();
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('getAvailability')
|
||||||
|
->willReturn(['available' => false, 'last_checked' => 0]);
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('test')
|
||||||
|
->willReturn(true);
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('setAvailability')
|
||||||
|
->with($this->equalTo(true));
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('mkdir');
|
||||||
|
|
||||||
|
$wrapper->mkdir('foobar');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage marked available, but throws StorageNotAvailableException
|
||||||
|
*
|
||||||
|
* @expectedException \OCP\Files\StorageNotAvailableException
|
||||||
|
*/
|
||||||
|
public function testAvailableThrowStorageNotAvailable() {
|
||||||
|
list($storage, $wrapper) = $this->getWrapperInstance();
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('getAvailability')
|
||||||
|
->willReturn(['available' => true, 'last_checked' => 0]);
|
||||||
|
$storage->expects($this->never())
|
||||||
|
->method('test');
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('mkdir')
|
||||||
|
->will($this->throwException(new \OCP\Files\StorageNotAvailableException()));
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('setAvailability')
|
||||||
|
->with($this->equalTo(false));
|
||||||
|
|
||||||
|
$wrapper->mkdir('foobar');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage available, but call fails
|
||||||
|
* Method failure does not indicate storage unavailability
|
||||||
|
*/
|
||||||
|
public function testAvailableFailure() {
|
||||||
|
list($storage, $wrapper) = $this->getWrapperInstance();
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('getAvailability')
|
||||||
|
->willReturn(['available' => true, 'last_checked' => 0]);
|
||||||
|
$storage->expects($this->never())
|
||||||
|
->method('test');
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('mkdir')
|
||||||
|
->willReturn(false);
|
||||||
|
$storage->expects($this->never())
|
||||||
|
->method('setAvailability');
|
||||||
|
|
||||||
|
$wrapper->mkdir('foobar');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage available, but throws exception
|
||||||
|
* Standard exception does not indicate storage unavailability
|
||||||
|
*
|
||||||
|
* @expectedException \Exception
|
||||||
|
*/
|
||||||
|
public function testAvailableThrow() {
|
||||||
|
list($storage, $wrapper) = $this->getWrapperInstance();
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('getAvailability')
|
||||||
|
->willReturn(['available' => true, 'last_checked' => 0]);
|
||||||
|
$storage->expects($this->never())
|
||||||
|
->method('test');
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('mkdir')
|
||||||
|
->will($this->throwException(new \Exception()));
|
||||||
|
$storage->expects($this->never())
|
||||||
|
->method('setAvailability');
|
||||||
|
|
||||||
|
$wrapper->mkdir('foobar');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue