MVC2.0: Introducing client library status values for instructing clients to download and activate a library; Operations to read and change client library status
This commit is contained in:
parent
6174229a1b
commit
4f0991eb67
|
@ -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<int>(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<Void> 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<Standalone<VectorRef<StringRef>>> listClientLibraries(Database db,
|
|||
return result;
|
||||
}
|
||||
|
||||
ACTOR Future<ClientLibStatus> getClientLibraryStatus(Database db, StringRef clientLibId) {
|
||||
state Key clientLibMetaKey = metadataKeyFromId(clientLibId.toString());
|
||||
state Transaction tr(db);
|
||||
loop {
|
||||
try {
|
||||
tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
|
||||
Optional<Value> 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<Void> 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<Value> 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
|
|
@ -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<Void> deleteClientLibrary(Database db, Standalone<StringRef> client
|
|||
// Returns metadata JSON of each library
|
||||
ACTOR Future<Standalone<VectorRef<StringRef>>> listClientLibraries(Database db, ClientLibFilter filter);
|
||||
|
||||
// Get the current status of an uploaded client library
|
||||
ACTOR Future<ClientLibStatus> getClientLibraryStatus(Database db, StringRef clientLibId);
|
||||
|
||||
// Change client library metadata status
|
||||
ACTOR Future<Void> changeClientLibraryStatus(Database db, StringRef clientLibId, ClientLibStatus newStatus);
|
||||
|
||||
} // namespace ClientLibManagement
|
||||
|
||||
#include "flow/unactorcompiler.h"
|
||||
|
|
|
@ -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<Void> 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<Void> 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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue