Merge pull request #25332 from nextcloud/fix-removing-remote-shares-when-the-remote-server-is-unreachable

Fix removing remote shares when the remote server is unreachable
pull/29568/head
Vincent Petry 2021-11-05 14:53:11 +07:00 committed by GitHub
commit 6d5f10eb57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 309 additions and 10 deletions

@ -28,6 +28,7 @@
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\TableNode;
require __DIR__ . '/../../vendor/autoload.php';
@ -39,6 +40,29 @@ class FederationContext implements Context, SnippetAcceptingContext {
use AppConfiguration;
use CommandLine;
/** @var string */
private static $phpFederatedServerPid = '';
/** @var string */
private $lastAcceptedRemoteShareId;
/**
* @BeforeScenario
* @AfterScenario
*
* The server is started also after the scenarios to ensure that it is
* properly cleaned up if stopped.
*/
public function startFederatedServer() {
if (self::$phpFederatedServerPid !== '') {
return;
}
$port = getenv('PORT_FED');
self::$phpFederatedServerPid = exec('php -S localhost:' . $port . ' -t ../../ >/dev/null & echo $!');
}
/**
* @BeforeScenario
*/
@ -93,6 +117,37 @@ class FederationContext implements Context, SnippetAcceptingContext {
$this->usingServer($previous);
}
/**
* @Then remote share :count is returned with
*
* @param int $number
* @param TableNode $body
*/
public function remoteShareXIsReturnedWith(int $number, TableNode $body) {
$this->theHTTPStatusCodeShouldBe('200');
$this->theOCSStatusCodeShouldBe('100');
if (!($body instanceof TableNode)) {
return;
}
$returnedShare = $this->getXmlResponse()->data[0];
if ($returnedShare->element) {
$returnedShare = $returnedShare->element[$number];
}
$defaultExpectedFields = [
'id' => 'A_NUMBER',
'remote_id' => 'A_NUMBER',
'accepted' => '1',
];
$expectedFields = array_merge($defaultExpectedFields, $body->getRowsHash());
foreach ($expectedFields as $field => $value) {
$this->assertFieldIsInReturnedShare($field, $value, $returnedShare);
}
}
/**
* @When /^User "([^"]*)" from server "(LOCAL|REMOTE)" accepts last pending share$/
* @param string $user
@ -109,6 +164,30 @@ class FederationContext implements Context, SnippetAcceptingContext {
$this->theHTTPStatusCodeShouldBe('200');
$this->theOCSStatusCodeShouldBe('100');
$this->usingServer($previous);
$this->lastAcceptedRemoteShareId = $share_id;
}
/**
* @When /^user "([^"]*)" deletes last accepted remote share$/
* @param string $user
*/
public function deleteLastAcceptedRemoteShare($user) {
$this->asAn($user);
$this->sendingToWith('DELETE', "/apps/files_sharing/api/v1/remote_shares/" . $this->lastAcceptedRemoteShareId, null);
}
/**
* @When /^remote server is stopped$/
*/
public function remoteServerIsStopped() {
if (self::$phpFederatedServerPid === '') {
return;
}
exec('kill ' . self::$phpFederatedServerPid);
self::$phpFederatedServerPid = '';
}
protected function resetAppConfigs() {

@ -278,13 +278,230 @@ Feature: federated
Scenario: List federated share from another server not accepted yet
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "user1" exists
# Rename file so it has a unique name in the target server (as the target
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
When As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
Then the list of returned shares has 0 shares
Scenario: List federated share from another server
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "user1" exists
# Rename file so it has a unique name in the target server (as the target
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
When As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
Then the list of returned shares has 1 shares
And remote share 0 is returned with
| remote | http://localhost:8180/ |
| name | /remote-share.txt |
| owner | user1 |
| user | user0 |
| mountpoint | /remote-share.txt |
| mimetype | text/plain |
| mtime | A_NUMBER |
| permissions | 27 |
| type | file |
| file_id | A_NUMBER |
Scenario: List federated share from another server no longer reachable
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "user1" exists
# Rename file so it has a unique name in the target server (as the target
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And remote server is stopped
When As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
Then the list of returned shares has 1 shares
And remote share 0 is returned with
| remote | http://localhost:8180/ |
| name | /remote-share.txt |
| owner | user1 |
| user | user0 |
| mountpoint | /remote-share.txt |
Scenario: List federated share from another server no longer reachable after caching the file entry
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "user1" exists
# Rename file so it has a unique name in the target server (as the target
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
# Checking that the file exists caches the file entry, which causes an
# exception to be thrown when getting the file info if the remote server is
# unreachable.
And as "user0" the file "/remote-share.txt" exists
And remote server is stopped
When As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
Then the list of returned shares has 1 shares
And remote share 0 is returned with
| remote | http://localhost:8180/ |
| name | /remote-share.txt |
| owner | user1 |
| user | user0 |
| mountpoint | /remote-share.txt |
Scenario: Delete federated share with another server
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "user1" exists
# Rename file so it has a unique name in the target server (as the target
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 1 shares
And Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 1 shares
And Using server "REMOTE"
When As an "user1"
And Deleting last share
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 0 shares
And Using server "LOCAL"
And as "user0" the file "/remote-share.txt" does not exist
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
Scenario: Delete federated share from another server
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "user1" exists
# Rename file so it has a unique name in the target server (as the target
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 1 shares
And Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 1 shares
When user "user0" deletes last accepted remote share
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And as "user0" the file "/remote-share.txt" does not exist
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
And Using server "REMOTE"
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 0 shares
Scenario: Delete federated share from another server no longer reachable
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "user1" exists
# Rename file so it has a unique name in the target server (as the target
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 1 shares
And remote server is stopped
When user "user0" deletes last accepted remote share
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And as "user0" the file "/remote-share.txt" does not exist
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
Scenario: Delete federated share file from another server
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "user1" exists
# Rename file so it has a unique name in the target server (as the target
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 1 shares
And Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 1 shares
When User "user0" deletes file "/remote-share.txt"
Then the HTTP status code should be "204"
And as "user0" the file "/remote-share.txt" does not exist
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
And Using server "REMOTE"
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 0 shares
Scenario: Delete federated share file from another server no longer reachable
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "user1" exists
# Rename file so it has a unique name in the target server (as the target
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 1 shares
And remote server is stopped
When User "user0" deletes file "/remote-share.txt"
Then the HTTP status code should be "204"
And as "user0" the file "/remote-share.txt" does not exist
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares

@ -38,11 +38,10 @@ php -S localhost:$PORT -t ../.. &
PHPPID=$!
echo $PHPPID
# The federated server is started and stopped by the tests themselves
PORT_FED=$((8180 + $EXECUTOR_NUMBER))
echo $PORT_FED
php -S localhost:$PORT_FED -t ../.. &
PHPPID_FED=$!
echo $PHPPID_FED
export PORT_FED
export TEST_SERVER_URL="http://localhost:$PORT/ocs/"
export TEST_SERVER_FED_URL="http://localhost:$PORT_FED/ocs/"
@ -65,7 +64,6 @@ vendor/bin/behat --strict -f junit -f pretty $TAGS $SCENARIO_TO_RUN
RESULT=$?
kill $PHPPID
kill $PHPPID_FED
if [ "$INSTALLED" == "true" ]; then

@ -3833,7 +3833,7 @@
</NoInterfaceProperties>
</file>
<file src="lib/private/Files/Storage/Wrapper/Availability.php">
<InvalidNullableReturnType occurrences="34">
<InvalidNullableReturnType occurrences="33">
<code>copy</code>
<code>copyFromStorage</code>
<code>file_exists</code>
@ -3850,7 +3850,6 @@
<code>getMimeType</code>
<code>getOwner</code>
<code>getPermissions</code>
<code>hasUpdated</code>
<code>hash</code>
<code>isCreatable</code>
<code>isDeletable</code>

@ -379,11 +379,15 @@ class Availability extends Wrapper {
/** {@inheritdoc} */
public function hasUpdated($path, $time) {
$this->checkAvailability();
if (!$this->isAvailable()) {
return false;
}
try {
return parent::hasUpdated($path, $time);
} catch (StorageNotAvailableException $e) {
$this->setUnavailable($e);
// set unavailable but don't rethrow
$this->setUnavailable(null);
return false;
}
}
@ -449,7 +453,7 @@ class Availability extends Wrapper {
/**
* @throws StorageNotAvailableException
*/
protected function setUnavailable(StorageNotAvailableException $e) {
protected function setUnavailable(?StorageNotAvailableException $e): void {
$delay = self::RECHECK_TTL_SEC;
if ($e instanceof StorageAuthException) {
$delay = max(
@ -459,7 +463,9 @@ class Availability extends Wrapper {
);
}
$this->getStorageCache()->setAvailability(false, $delay);
throw $e;
if ($e !== null) {
throw $e;
}
}