collaborative posix test (#672)

* collaborative posix test

* fix after review
This commit is contained in:
Viktor Scharf
2025-05-05 10:21:11 +02:00
committed by GitHub
parent 7c8174bb80
commit 308f801f03
11 changed files with 142 additions and 23 deletions
+6 -2
View File
@@ -132,6 +132,7 @@ config = {
"suites": [ "suites": [
"apiGraph", "apiGraph",
"apiServiceAvailability", "apiServiceAvailability",
"collaborativePosix",
], ],
"skip": False, "skip": False,
"withRemotePhp": [True], "withRemotePhp": [True],
@@ -939,12 +940,13 @@ def localApiTests(name, suites, storage = "decomposed", extra_environment = {},
"SEND_SCENARIO_LINE_REFERENCES": True, "SEND_SCENARIO_LINE_REFERENCES": True,
"STORAGE_DRIVER": storage, "STORAGE_DRIVER": storage,
"BEHAT_SUITES": ",".join(suites), "BEHAT_SUITES": ",".join(suites),
"BEHAT_FILTER_TAGS": "~@skip&&~@skipOnGraph&&~@skipOnOpencloud-%s-Storage" % storage, "BEHAT_FILTER_TAGS": "~@skip&&~@skipOnOpencloud-%s-Storage" % storage,
"EXPECTED_FAILURES_FILE": expected_failures_file, "EXPECTED_FAILURES_FILE": expected_failures_file,
"UPLOAD_DELETE_WAIT_TIME": "1" if storage == "owncloud" else 0, "UPLOAD_DELETE_WAIT_TIME": "1" if storage == "owncloud" else 0,
"OC_WRAPPER_URL": "http://%s:5200" % OC_SERVER_NAME, "OC_WRAPPER_URL": "http://%s:5200" % OC_SERVER_NAME,
"WITH_REMOTE_PHP": with_remote_php, "WITH_REMOTE_PHP": with_remote_php,
"COLLABORATION_SERVICE_URL": "http://wopi-fakeoffice:9300", "COLLABORATION_SERVICE_URL": "http://wopi-fakeoffice:9300",
"OC_STORAGE_PATH": "$HOME/.opencloud/storage/users/users",
} }
for item in extra_environment: for item in extra_environment:
@@ -1107,7 +1109,7 @@ def coreApiTests(ctx, part_number = 1, number_of_parts = 1, with_remote_php = Fa
storage = "posix" storage = "posix"
if "[decomposed]" in ctx.build.title.lower(): if "[decomposed]" in ctx.build.title.lower():
storage = "decomposed" storage = "decomposed"
filterTags = "~@skipOnGraph&&~@skipOnOpencloud-%s-Storage" % storage filterTags = "~@skipOnOpencloud-%s-Storage" % storage
test_dir = "%s/tests/acceptance" % dirs["base"] test_dir = "%s/tests/acceptance" % dirs["base"]
expected_failures_file = "%s/expected-failures-API-on-%s-storage.md" % (test_dir, storage) expected_failures_file = "%s/expected-failures-API-on-%s-storage.md" % (test_dir, storage)
@@ -2001,6 +2003,8 @@ def opencloudServer(storage = "decomposed", accounts_hash_difficulty = 4, depend
}, },
}, },
"commands": [ "commands": [
"apt-get update",
"apt-get install -y inotify-tools",
"%s init --insecure true" % dirs["opencloudBin"], "%s init --insecure true" % dirs["opencloudBin"],
"cat $OC_CONFIG_DIR/opencloud.yaml", "cat $OC_CONFIG_DIR/opencloud.yaml",
"cp tests/config/woodpecker/app-registry.yaml $OC_CONFIG_DIR/app-registry.yaml", "cp tests/config/woodpecker/app-registry.yaml $OC_CONFIG_DIR/app-registry.yaml",
@@ -20,9 +20,7 @@
namespace TestHelpers; namespace TestHelpers;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use TestHelpers\OcConfigHelper; use TestHelpers\OcConfigHelper;
+61 -16
View File
@@ -34,6 +34,15 @@ class CliContext implements Context {
private FeatureContext $featureContext; private FeatureContext $featureContext;
private SpacesContext $spacesContext; private SpacesContext $spacesContext;
/**
* opencloud user storage path
*
* @return string
*/
public static function getStoragePath(): string {
return getenv('OC_STORAGE_PATH') ?: '/var/lib/opencloud/storage/users/users';
}
/** /**
* @BeforeScenario * @BeforeScenario
* *
@@ -85,8 +94,8 @@ class CliContext implements Context {
): void { ): void {
$command = "idm resetpassword -u $user"; $command = "idm resetpassword -u $user";
$body = [ $body = [
"command" => $command, "command" => $command,
"inputs" => [$password, $password] "inputs" => [$password, $password]
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
@@ -105,7 +114,7 @@ class CliContext implements Context {
$path = $this->featureContext->getStorageUsersRoot(); $path = $this->featureContext->getStorageUsersRoot();
$command = "trash purge-empty-dirs -p $path --dry-run=false"; $command = "trash purge-empty-dirs -p $path --dry-run=false";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -119,7 +128,7 @@ class CliContext implements Context {
$path = $this->featureContext->getStorageUsersRoot(); $path = $this->featureContext->getStorageUsersRoot();
$command = "backup consistency -p $path"; $command = "backup consistency -p $path";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -139,7 +148,7 @@ class CliContext implements Context {
$user = $this->featureContext->getActualUserName($user); $user = $this->featureContext->getActualUserName($user);
$command = "auth-app create --user-name=$user --expiration=$expirationTime"; $command = "auth-app create --user-name=$user --expiration=$expirationTime";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -159,7 +168,7 @@ class CliContext implements Context {
$user = $this->featureContext->getActualUserName($user); $user = $this->featureContext->getActualUserName($user);
$command = "auth-app create --user-name=$user --expiration=$expirationTime"; $command = "auth-app create --user-name=$user --expiration=$expirationTime";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$response = CliHelper::runCommand($body); $response = CliHelper::runCommand($body);
@@ -182,7 +191,7 @@ class CliContext implements Context {
$path = $this->featureContext->getStorageUsersRoot(); $path = $this->featureContext->getStorageUsersRoot();
$command = "revisions purge -p $path --dry-run=false"; $command = "revisions purge -p $path --dry-run=false";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -201,7 +210,7 @@ class CliContext implements Context {
$fileId = $this->spacesContext->getFileId($user, $space, $file); $fileId = $this->spacesContext->getFileId($user, $space, $file);
$command = "revisions purge -p $path -r $fileId --dry-run=false"; $command = "revisions purge -p $path -r $fileId --dry-run=false";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -214,7 +223,7 @@ class CliContext implements Context {
public function theAdministratorReindexesAllSpacesUsingTheCli(): void { public function theAdministratorReindexesAllSpacesUsingTheCli(): void {
$command = "search index --all-spaces"; $command = "search index --all-spaces";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -230,7 +239,7 @@ class CliContext implements Context {
$spaceId = $this->spacesContext->getSpaceIdByName($this->featureContext->getAdminUsername(), $spaceName); $spaceId = $this->spacesContext->getSpaceIdByName($this->featureContext->getAdminUsername(), $spaceName);
$command = "search index --space $spaceId"; $command = "search index --space $spaceId";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -248,7 +257,7 @@ class CliContext implements Context {
$spaceId = $this->spacesContext->getSpaceIdByName($adminUsername, $space); $spaceId = $this->spacesContext->getSpaceIdByName($adminUsername, $space);
$command = "revisions purge -p $path -r $spaceId --dry-run=false"; $command = "revisions purge -p $path -r $spaceId --dry-run=false";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -306,7 +315,7 @@ class CliContext implements Context {
} }
$command = "storage-users uploads sessions --json $flag"; $command = "storage-users uploads sessions --json $flag";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -326,7 +335,7 @@ class CliContext implements Context {
$flagString = trim($flag); $flagString = trim($flag);
$command = "storage-users uploads sessions $flagString --clean --json"; $command = "storage-users uploads sessions $flagString --clean --json";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -339,7 +348,7 @@ class CliContext implements Context {
public function theAdministratorRestartsTheUploadSessionsThatAreInPostprocessing(): void { public function theAdministratorRestartsTheUploadSessionsThatAreInPostprocessing(): void {
$command = "storage-users uploads sessions --processing --restart --json"; $command = "storage-users uploads sessions --processing --restart --json";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -365,7 +374,7 @@ class CliContext implements Context {
$command = "storage-users uploads sessions --id=$uploadId --restart --json"; $command = "storage-users uploads sessions --id=$uploadId --restart --json";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$this->featureContext->setResponse(CliHelper::runCommand($body)); $this->featureContext->setResponse(CliHelper::runCommand($body));
} }
@@ -431,9 +440,45 @@ class CliContext implements Context {
public function cleanUploadsSessions(): void { public function cleanUploadsSessions(): void {
$command = "storage-users uploads sessions --clean"; $command = "storage-users uploads sessions --clean";
$body = [ $body = [
"command" => $command "command" => $command
]; ];
$response = CliHelper::runCommand($body); $response = CliHelper::runCommand($body);
Assert::assertEquals("200", $response->getStatusCode(), "Failed to clean upload sessions"); Assert::assertEquals("200", $response->getStatusCode(), "Failed to clean upload sessions");
} }
/**
* @When the administrator creates folder :folder for user :user on the POSIX filesystem
*
* @param string $folder
* @param string $user
*
* @return void
*/
public function theAdministratorCreatesFolder(string $folder, string $user): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getStoragePath();
$body = [
"command" => "mkdir -p $storagePath/$userUuid/$folder",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
sleep(1);
}
/**
* @When the administrator lists the content of the POSIX storage folder of user :user
*
* @param string $user
*
* @return void
*/
public function theAdministratorCheckUsersFolder(string $user): void {
$userUuid = $this->featureContext->getUserIdByUserName($user);
$storagePath = $this->getStoragePath();
$body = [
"command" => "ls -la $storagePath/$userUuid",
"raw" => true
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
} }
+9
View File
@@ -458,6 +458,15 @@ default:
- TrashbinContext: - TrashbinContext:
- SpacesTUSContext: - SpacesTUSContext:
collaborativePosix:
paths:
- "%paths.base%/../features/collaborativePosix"
context: *common_ldap_suite_context
contexts:
- FeatureContext: *common_feature_context_params
- CliContext:
- OcConfigContext:
coreApiMain: coreApiMain:
paths: paths:
- "%paths.base%/../features/coreApiMain" - "%paths.base%/../features/coreApiMain"
+2 -2
View File
@@ -48,8 +48,8 @@ else
SEARCH_EXTRACTOR_TYPE := basic SEARCH_EXTRACTOR_TYPE := basic
endif endif
# default to decomposedfs # default to posix
STORAGE_DRIVER ?= decomposed STORAGE_DRIVER ?= posix
ifeq ($(STORAGE_DRIVER),posix) ifeq ($(STORAGE_DRIVER),posix)
# posix requires a additional driver config # posix requires a additional driver config
COMPOSE_FILE := $(COMPOSE_FILE):src/posix.yml COMPOSE_FILE := $(COMPOSE_FILE):src/posix.yml
@@ -10,6 +10,7 @@ services:
WITH_WRAPPER: $WITH_WRAPPER WITH_WRAPPER: $WITH_WRAPPER
OC_URL: "https://opencloud-server:9200" OC_URL: "https://opencloud-server:9200"
STORAGE_USERS_DRIVER: $STORAGE_DRIVER STORAGE_USERS_DRIVER: $STORAGE_DRIVER
STORAGE_USERS_POSIX_WATCH_FS: "true"
STORAGE_USERS_DRIVER_LOCAL_ROOT: /srv/app/tmp/opencloud/local/root STORAGE_USERS_DRIVER_LOCAL_ROOT: /srv/app/tmp/opencloud/local/root
STORAGE_USERS_DRIVER_OC_ROOT: /srv/app/tmp/opencloud/storage/users STORAGE_USERS_DRIVER_OC_ROOT: /srv/app/tmp/opencloud/storage/users
STORAGE_SYSTEM_DRIVER_OC_ROOT: /srv/app/tmp/opencloud/storage/metadata STORAGE_SYSTEM_DRIVER_OC_ROOT: /srv/app/tmp/opencloud/storage/metadata
@@ -6,6 +6,7 @@ FROM opencloudeu/opencloud:${OC_IMAGE_TAG} AS opencloud
FROM ubuntu:22.04 FROM ubuntu:22.04
COPY --from=opencloud /usr/bin/opencloud /usr/bin/opencloud COPY --from=opencloud /usr/bin/opencloud /usr/bin/opencloud
RUN apt-get update && apt-get install -y inotify-tools
COPY ["./serve-opencloud.sh", "/usr/bin/serve-opencloud"] COPY ["./serve-opencloud.sh", "/usr/bin/serve-opencloud"]
RUN chmod +x /usr/bin/serve-opencloud RUN chmod +x /usr/bin/serve-opencloud
+1
View File
@@ -6,3 +6,4 @@ services:
STORAGE_USERS_DRIVER: posix STORAGE_USERS_DRIVER: posix
# posix requires a shared cache store # posix requires a shared cache store
STORAGE_USERS_ID_CACHE_STORE: "nats-js-kv" STORAGE_USERS_ID_CACHE_STORE: "nats-js-kv"
STORAGE_USERS_POSIX_WATCH_FS: "true"
@@ -0,0 +1,15 @@
@env-config @skipOnOpencloud-decomposed-Storage @skipOnOpencloud-decomposeds3-Storage
Feature: create a resources using collaborative posixfs
Background:
Given the config "STORAGE_USERS_POSIX_WATCH_FS" has been set to "true"
And user "Alice" has been created with default attributes
Scenario: create folder
Given user "Alice" has uploaded file with content "content" to "textfile.txt"
When the administrator creates folder "myFolder" for user "Alice" on the POSIX filesystem
Then the command should be successful
When the administrator lists the content of the POSIX storage folder of user "Alice"
Then the command output should contain "myFolder"
And as "Alice" folder "/myFolder" should exist
+33
View File
@@ -271,3 +271,36 @@ func RunCommand(command string, inputs []string) (int, string) {
return c.ProcessState.ExitCode(), cmdOutput return c.ProcessState.ExitCode(), cmdOutput
} }
func RunRawCommand(command string, inputs []string) (int, string) {
logs := new(strings.Builder)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
fmt.Print("Running command: ", command)
c := exec.CommandContext(ctx, "bash", "-c", command)
ptyF, err := pty.Start(c)
if err != nil {
log.Panic(err)
}
defer ptyF.Close()
for _, input := range inputs {
fmt.Fprintf(ptyF, "%s\n", input)
}
var cmdOutput string
if err := c.Wait(); err != nil {
if ctx.Err() == context.DeadlineExceeded {
cmdOutput = "Command timed out:\n"
}
}
io.Copy(logs, ptyF)
cmdOutput += logs.String()
cmdOutput = strings.TrimLeft(cmdOutput, strings.Join(inputs, "\r\n"))
return c.ProcessState.ExitCode(), cmdOutput
}
+13 -1
View File
@@ -198,7 +198,19 @@ func CommandHandler(res http.ResponseWriter, req *http.Request) {
} }
} }
} }
raw := false
if r, ok := body["raw"].(bool); ok {
raw = r
}
exitCode, output := opencloud.RunCommand(command, stdIn) var exitCode int
var output string
if raw {
exitCode, output = opencloud.RunRawCommand(command, stdIn)
} else {
exitCode, output = opencloud.RunCommand(command, stdIn)
}
sendCmdResponse(res, exitCode, output) sendCmdResponse(res, exitCode, output)
} }