From 4f0991eb675dc80393784c01f5312ba29597e71e Mon Sep 17 00:00:00 2001 From: Vaidas Gasiunas Date: Tue, 12 Oct 2021 17:29:09 +0200 Subject: [PATCH] MVC2.0: Introducing client library status values for instructing clients to download and activate a library; Operations to read and change client library status --- fdbclient/ClientLibManagement.actor.cpp | 70 ++++++++++++++++++- fdbclient/ClientLibManagement.actor.h | 8 +++ .../ClientLibManagementWorkload.actor.cpp | 37 ++++++++++ 3 files changed, 112 insertions(+), 3 deletions(-) diff --git a/fdbclient/ClientLibManagement.actor.cpp b/fdbclient/ClientLibManagement.actor.cpp index 8b24956ee3..cce3ab783e 100644 --- a/fdbclient/ClientLibManagement.actor.cpp +++ b/fdbclient/ClientLibManagement.actor.cpp @@ -46,7 +46,7 @@ struct ClientLibBinaryInfo { #define ASSERT_INDEX_IN_RANGE(idx, arr) ASSERT(idx >= 0 && idx < sizeof(arr) / sizeof(arr[0])) const std::string& getStatusName(ClientLibStatus status) { - static const std::string statusNames[] = { "disabled", "available", "uploading" }; + static const std::string statusNames[] = { "disabled", "available", "uploading", "download", "active" }; int idx = static_cast(status); ASSERT_INDEX_IN_RANGE(idx, statusNames); return statusNames[idx]; @@ -123,7 +123,13 @@ ClientLibChecksumAlg getChecksumAlgByName(std::string_view checksumAlgName) { namespace { bool isValidTargetStatus(ClientLibStatus status) { - return status == ClientLibStatus::AVAILABLE || status == ClientLibStatus::DISABLED; + return status == ClientLibStatus::AVAILABLE || status == ClientLibStatus::DISABLED || + status == ClientLibStatus::DOWNLOAD || status == ClientLibStatus::ACTIVE; +} + +bool isAvailableForDownload(ClientLibStatus status) { + return status == ClientLibStatus::AVAILABLE || status == ClientLibStatus::DOWNLOAD || + status == ClientLibStatus::ACTIVE; } json_spirit::mObject parseMetadataJson(StringRef metadataString) { @@ -489,7 +495,7 @@ ACTOR Future downloadClientLibrary(Database db, } // Allow downloading only libraries in the available state - if (getStatusByName(getMetadataStrAttr(metadataJson, CLIENTLIB_ATTR_STATUS)) != ClientLibStatus::AVAILABLE) { + if (!isAvailableForDownload(getStatusByName(getMetadataStrAttr(metadataJson, CLIENTLIB_ATTR_STATUS)))) { throw client_lib_not_available(); } @@ -707,4 +713,62 @@ ACTOR Future>> listClientLibraries(Database db, return result; } +ACTOR Future getClientLibraryStatus(Database db, StringRef clientLibId) { + state Key clientLibMetaKey = metadataKeyFromId(clientLibId.toString()); + state Transaction tr(db); + loop { + try { + tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS); + Optional metadataOpt = wait(tr.get(clientLibMetaKey)); + if (!metadataOpt.present()) { + TraceEvent(SevWarnAlways, "ClientLibraryNotFound").detail("Key", clientLibMetaKey); + throw client_lib_not_found(); + } + json_spirit::mObject metadataJson = parseMetadataJson(metadataOpt.get()); + return getStatusByName(getMetadataStrAttr(metadataJson, CLIENTLIB_ATTR_STATUS)); + } catch (Error& e) { + wait(tr.onError(e)); + } + } +} + +ACTOR Future changeClientLibraryStatus(Database db, StringRef clientLibId, ClientLibStatus newStatus) { + state Key clientLibMetaKey = metadataKeyFromId(clientLibId.toString()); + state json_spirit::mObject metadataJson; + state std::string jsStr; + + if (!isValidTargetStatus(newStatus)) { + TraceEvent(SevWarnAlways, "ClientLibraryInvalidMetadata") + .detail("Reason", "InvalidTargetStatus") + .detail("Status", getStatusName(newStatus)); + throw client_lib_invalid_metadata(); + } + + loop { + state Transaction tr(db); + try { + tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + Optional metadataOpt = wait(tr.get(clientLibMetaKey)); + if (!metadataOpt.present()) { + TraceEvent(SevWarnAlways, "ClientLibraryNotFound").detail("Key", clientLibMetaKey); + throw client_lib_not_found(); + } + metadataJson = parseMetadataJson(metadataOpt.get()); + metadataJson[CLIENTLIB_ATTR_STATUS] = getStatusName(newStatus); + jsStr = json_spirit::write_string(json_spirit::mValue(metadataJson)); + tr.set(clientLibMetaKey, ValueRef(jsStr)); + wait(tr.commit()); + break; + } catch (Error& e) { + if (e.code() == error_code_client_lib_not_found) { + throw; + } + wait(tr.onError(e)); + } + } + + TraceEvent("ClientLibraryStatusChanged").detail("Key", clientLibMetaKey).detail("Status", getStatusName(newStatus)); + return Void(); +} + } // namespace ClientLibManagement \ No newline at end of file diff --git a/fdbclient/ClientLibManagement.actor.h b/fdbclient/ClientLibManagement.actor.h index eaea5ccbf8..fed7be7f79 100644 --- a/fdbclient/ClientLibManagement.actor.h +++ b/fdbclient/ClientLibManagement.actor.h @@ -37,6 +37,8 @@ enum class ClientLibStatus { DISABLED = 0, AVAILABLE, // 1 UPLOADING, // 2 + DOWNLOAD, // 3 + ACTIVE, // 4 COUNT // must be the last one }; @@ -133,6 +135,12 @@ ACTOR Future deleteClientLibrary(Database db, Standalone client // Returns metadata JSON of each library ACTOR Future>> listClientLibraries(Database db, ClientLibFilter filter); +// Get the current status of an uploaded client library +ACTOR Future getClientLibraryStatus(Database db, StringRef clientLibId); + +// Change client library metadata status +ACTOR Future changeClientLibraryStatus(Database db, StringRef clientLibId, ClientLibStatus newStatus); + } // namespace ClientLibManagement #include "flow/unactorcompiler.h" diff --git a/fdbserver/workloads/ClientLibManagementWorkload.actor.cpp b/fdbserver/workloads/ClientLibManagementWorkload.actor.cpp index f259f4c756..7e8a8a2d67 100644 --- a/fdbserver/workloads/ClientLibManagementWorkload.actor.cpp +++ b/fdbserver/workloads/ClientLibManagementWorkload.actor.cpp @@ -107,6 +107,8 @@ struct ClientLibManagementWorkload : public TestWorkload { wait(testClientLibListAfterUpload(self, cx)); wait(testDownloadClientLib(self, cx)); wait(testClientLibDownloadNotExisting(self, cx)); + wait(testChangeClientLibStatusErrors(self, cx)); + wait(testDisableClientLib(self, cx)); wait(testDeleteClientLib(self, cx)); wait(testUploadedClientLibInList(self, cx, ClientLibFilter(), false, "No filter, after delete")); return Void(); @@ -321,6 +323,41 @@ struct ClientLibManagementWorkload : public TestWorkload { return Void(); } + ACTOR static Future testChangeClientLibStatusErrors(ClientLibManagementWorkload* self, Database cx) { + wait(testExpectedError(changeClientLibraryStatus(cx, self->uploadedClientLibId, ClientLibStatus::UPLOADING), + "Setting invalid client library status", + client_lib_invalid_metadata(), + &self->success)); + + wait(testExpectedError(changeClientLibraryStatus(cx, "notExistingClientLib"_sr, ClientLibStatus::DOWNLOAD), + "Changing not existing client library status", + client_lib_not_found(), + &self->success)); + return Void(); + } + + ACTOR static Future testDisableClientLib(ClientLibManagementWorkload* self, Database cx) { + state std::string destFileName = format("clientLibDownload%d", self->clientId); + + // Set disabled status on the uploaded library + wait(changeClientLibraryStatus(cx, self->uploadedClientLibId, ClientLibStatus::DISABLED)); + state ClientLibStatus newStatus = wait(getClientLibraryStatus(cx, self->uploadedClientLibId)); + if (newStatus != ClientLibStatus::DISABLED) { + TraceEvent(SevError, "ClientLibDisableClientLibFailed") + .detail("Reason", "Unexpected status") + .detail("Expected", ClientLibStatus::DISABLED) + .detail("Actual", newStatus); + self->success = false; + } + + // It should not be possible to download a disabled client library + wait(testExpectedError(downloadClientLibrary(cx, self->uploadedClientLibId, StringRef(destFileName)), + "Downloading disabled client library", + client_lib_not_available(), + &self->success)); + return Void(); + } + /* ---------------------------------------------------------------- * Utility methods */