/* * TenantCommands.actor.cpp * * This source file is part of the FoundationDB open source project * * Copyright 2013-2022 Apple Inc. and the FoundationDB project authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fdbcli/fdbcli.actor.h" #include "fdbclient/FDBOptions.g.h" #include "fdbclient/IClientApi.h" #include "fdbclient/Knobs.h" #include "fdbclient/ManagementAPI.actor.h" #include "fdbclient/Schemas.h" #include "flow/Arena.h" #include "flow/FastRef.h" #include "flow/ThreadHelper.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. namespace fdb_cli { const KeyRangeRef tenantSpecialKeyRange(LiteralStringRef("\xff\xff/management/tenant_map/"), LiteralStringRef("\xff\xff/management/tenant_map0")); // createtenant command ACTOR Future<bool> createTenantCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens) { if (tokens.size() != 2) { printUsage(tokens[0]); return false; } state Key tenantNameKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(tokens[1]); state Reference<ITransaction> tr = db->createTransaction(); state bool doneExistenceCheck = false; loop { tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); try { if (!doneExistenceCheck) { Optional<Value> existingTenant = wait(safeThreadFutureToFuture(tr->get(tenantNameKey))); if (existingTenant.present()) { throw tenant_already_exists(); } doneExistenceCheck = true; } tr->set(tenantNameKey, ValueRef()); wait(safeThreadFutureToFuture(tr->commit())); break; } catch (Error& e) { state Error err(e); if (e.code() == error_code_special_keys_api_failure) { std::string errorMsgStr = wait(fdb_cli::getSpecialKeysFailureErrorMessage(tr)); fprintf(stderr, "ERROR: %s\n", errorMsgStr.c_str()); return false; } wait(safeThreadFutureToFuture(tr->onError(err))); } } printf("The tenant `%s' has been created\n", printable(tokens[1]).c_str()); return true; } CommandFactory createTenantFactory("createtenant", CommandHelp("createtenant <TENANT_NAME>", "creates a new tenant in the cluster", "Creates a new tenant in the cluster with the specified name.")); // deletetenant command ACTOR Future<bool> deleteTenantCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens) { if (tokens.size() != 2) { printUsage(tokens[0]); return false; } state Key tenantNameKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(tokens[1]); state Reference<ITransaction> tr = db->createTransaction(); state bool doneExistenceCheck = false; loop { tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); try { if (!doneExistenceCheck) { Optional<Value> existingTenant = wait(safeThreadFutureToFuture(tr->get(tenantNameKey))); if (!existingTenant.present()) { throw tenant_not_found(); } doneExistenceCheck = true; } tr->clear(tenantNameKey); wait(safeThreadFutureToFuture(tr->commit())); break; } catch (Error& e) { state Error err(e); if (e.code() == error_code_special_keys_api_failure) { std::string errorMsgStr = wait(fdb_cli::getSpecialKeysFailureErrorMessage(tr)); fprintf(stderr, "ERROR: %s\n", errorMsgStr.c_str()); return false; } wait(safeThreadFutureToFuture(tr->onError(err))); } } printf("The tenant `%s' has been deleted\n", printable(tokens[1]).c_str()); return true; } CommandFactory deleteTenantFactory( "deletetenant", CommandHelp( "deletetenant <TENANT_NAME>", "deletes a tenant from the cluster", "Deletes a tenant from the cluster. Deletion will be allowed only if the specified tenant contains no data.")); // listtenants command ACTOR Future<bool> listTenantsCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens) { if (tokens.size() > 4) { printUsage(tokens[0]); return false; } StringRef beginTenant = ""_sr; StringRef endTenant = "\xff\xff"_sr; state int limit = 100; if (tokens.size() >= 2) { beginTenant = tokens[1]; } if (tokens.size() >= 3) { endTenant = tokens[2]; if (endTenant <= beginTenant) { fprintf(stderr, "ERROR: end must be larger than begin"); return false; } } if (tokens.size() == 4) { int n = 0; if (sscanf(tokens[3].toString().c_str(), "%d%n", &limit, &n) != 1 || n != tokens[3].size()) { fprintf(stderr, "ERROR: invalid limit %s\n", tokens[3].toString().c_str()); return false; } } state Key beginTenantKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(beginTenant); state Key endTenantKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(endTenant); state Reference<ITransaction> tr = db->createTransaction(); loop { try { RangeResult tenants = wait(safeThreadFutureToFuture( tr->getRange(firstGreaterOrEqual(beginTenantKey), firstGreaterOrEqual(endTenantKey), limit))); if (tenants.empty()) { if (tokens.size() == 1) { printf("The cluster has no tenants\n"); } else { printf("The cluster has no tenants in the specified range\n"); } } int index = 0; for (auto tenant : tenants) { printf(" %d. %s\n", ++index, printable(tenant.key.removePrefix(fdb_cli::tenantSpecialKeyRange.begin)).c_str()); } return true; } catch (Error& e) { state Error err(e); if (e.code() == error_code_special_keys_api_failure) { std::string errorMsgStr = wait(fdb_cli::getSpecialKeysFailureErrorMessage(tr)); fprintf(stderr, "ERROR: %s\n", errorMsgStr.c_str()); return false; } wait(safeThreadFutureToFuture(tr->onError(err))); } } } CommandFactory listTenantsFactory( "listtenants", CommandHelp("listtenants [BEGIN] [END] [LIMIT]", "print a list of tenants in the cluster", "Print a list of tenants in the cluster. Only tenants in the range [BEGIN] - [END] will be printed. " "The number of tenants to print can be specified using the [LIMIT] parameter, which defaults to 100.")); // gettenant command ACTOR Future<bool> getTenantCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens) { if (tokens.size() != 2) { printUsage(tokens[0]); return false; } state Key tenantNameKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(tokens[1]); state Reference<ITransaction> tr = db->createTransaction(); loop { try { Optional<Value> tenant = wait(safeThreadFutureToFuture(tr->get(tenantNameKey))); if (!tenant.present()) { throw tenant_not_found(); } json_spirit::mValue jsonObject; json_spirit::read_string(tenant.get().toString(), jsonObject); JSONDoc doc(jsonObject); int64_t id; std::string prefix; doc.get("id", id); doc.get("prefix", prefix); printf(" id: %" PRId64 "\n", id); printf(" prefix: %s\n", printable(prefix).c_str()); return true; } catch (Error& e) { state Error err(e); if (e.code() == error_code_special_keys_api_failure) { std::string errorMsgStr = wait(fdb_cli::getSpecialKeysFailureErrorMessage(tr)); fprintf(stderr, "ERROR: %s\n", errorMsgStr.c_str()); return false; } wait(safeThreadFutureToFuture(tr->onError(err))); } } } CommandFactory getTenantFactory("gettenant", CommandHelp("gettenant <TENANT_NAME>", "prints the metadata for a tenant", "Prints the metadata for a tenant.")); } // namespace fdb_cli