diff --git a/bindings/c/test/apitester/run_c_api_tests.py b/bindings/c/test/apitester/run_c_api_tests.py index 4440237adf..647a796076 100755 --- a/bindings/c/test/apitester/run_c_api_tests.py +++ b/bindings/c/test/apitester/run_c_api_tests.py @@ -31,19 +31,24 @@ import random import string import toml -sys.path[:0] = [os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "tests", "TestRunner")] - # fmt: off from tmp_cluster import TempCluster from local_cluster import TLSConfig # fmt: on +sys.path[:0] = [ + os.path.join( + os.path.dirname(__file__), "..", "..", "..", "..", "tests", "TestRunner" + ) +] TESTER_STATS_INTERVAL_SEC = 5 def random_string(len): - return "".join(random.choice(string.ascii_letters + string.digits) for i in range(len)) + return "".join( + random.choice(string.ascii_letters + string.digits) for i in range(len) + ) def get_logger(): @@ -77,7 +82,9 @@ def dump_client_logs(log_dir): def run_tester(args, cluster, test_file): build_dir = Path(args.build_dir).resolve() tester_binary = Path(args.api_tester_bin).resolve() - external_client_library = build_dir.joinpath("bindings", "c", "libfdb_c_external.so") + external_client_library = build_dir.joinpath( + "bindings", "c", "libfdb_c_external.so" + ) log_dir = Path(cluster.log).joinpath("client") log_dir.mkdir(exist_ok=True) cmd = [ @@ -141,7 +148,9 @@ def run_tester(args, cluster, test_file): reason = signal.Signals(-ret_code).name else: reason = "exit code: %d" % ret_code - get_logger().error("\n'%s' did not complete succesfully (%s)" % (cmd[0], reason)) + get_logger().error( + "\n'%s' did not complete succesfully (%s)" % (cmd[0], reason) + ) if log_dir is not None and not args.disable_log_dump: dump_client_logs(log_dir) @@ -160,7 +169,9 @@ class TestConfig: self.server_chain_len = server_config.get("tls_server_chain_len", 3) self.min_num_processes = server_config.get("min_num_processes", 1) self.max_num_processes = server_config.get("max_num_processes", 3) - self.num_processes = random.randint(self.min_num_processes, self.max_num_processes) + self.num_processes = random.randint( + self.min_num_processes, self.max_num_processes + ) def run_test(args, test_file): @@ -210,9 +221,20 @@ def run_tests(args): def parse_args(argv): parser = argparse.ArgumentParser(description="FoundationDB C API Tester") - parser.add_argument("--build-dir", "-b", type=str, required=True, help="FDB build directory") - parser.add_argument("--api-tester-bin", type=str, help="Path to the fdb_c_api_tester executable.", required=True) - parser.add_argument("--external-client-library", type=str, help="Path to the external client library.") + parser.add_argument( + "--build-dir", "-b", type=str, required=True, help="FDB build directory" + ) + parser.add_argument( + "--api-tester-bin", + type=str, + help="Path to the fdb_c_api_tester executable.", + required=True, + ) + parser.add_argument( + "--external-client-library", + type=str, + help="Path to the external client library.", + ) parser.add_argument( "--retain-client-lib-copies", action="store_true", diff --git a/bindings/c/test/mako/admin_server.cpp b/bindings/c/test/mako/admin_server.cpp index 59de4aec72..306d4f906a 100644 --- a/bindings/c/test/mako/admin_server.cpp +++ b/bindings/c/test/mako/admin_server.cpp @@ -22,6 +22,7 @@ #include "future.hpp" #include "logger.hpp" #include "tenant.hpp" +#include "time.hpp" #include "utils.hpp" #include #include @@ -30,39 +31,19 @@ #include #include #include +#include #include #include -#include -#include -#include -#include -#include +#include #include "rapidjson/document.h" extern thread_local mako::Logger logr; -using oarchive = boost::archive::binary_oarchive; -using iarchive = boost::archive::binary_iarchive; - namespace { -template -void sendObject(boost::process::pstream& pipe, T obj) { - oarchive oa(pipe); - oa << obj; -} - -template -T receiveObject(boost::process::pstream& pipe) { - iarchive ia(pipe); - T obj; - ia >> obj; - return obj; -} - fdb::Database getOrCreateDatabase(std::map& db_map, const std::string& cluster_file) { auto iter = db_map.find(cluster_file); if (iter == db_map.end()) { @@ -122,35 +103,54 @@ void AdminServer::start() { } }); - while (true) { + bool stop = false; + while (!stop) { try { auto req = receiveObject(pipe_to_server); - if (setup_error) { - sendObject(pipe_to_client, Response{ setup_error }); - } else if (boost::get(&req)) { - sendObject(pipe_to_client, Response{}); - } else if (boost::get(&req)) { - logr.info("server was requested to stop"); - sendObject(pipe_to_client, Response{}); - return; - } else if (auto p = boost::get(&req)) { - logr.info("received request to batch-create tenants [{}:{}) in database '{}'", - p->id_begin, - p->id_end, - p->cluster_file); - auto err_msg = createTenant(getOrCreateDatabase(databases, p->cluster_file), p->id_begin, p->id_end); - sendObject(pipe_to_client, Response{ std::move(err_msg) }); - } else if (auto p = boost::get(&req)) { - logr.info("received request to batch-delete tenants [{}:{}) in database '{}'", - p->id_begin, - p->id_end, - p->cluster_file); - auto err_msg = deleteTenant(getOrCreateDatabase(databases, p->cluster_file), p->id_begin, p->id_end); - sendObject(pipe_to_client, Response{ std::move(err_msg) }); - } else { - logr.error("unknown request received"); - sendObject(pipe_to_client, Response{ std::string("unknown request type") }); - } + boost::apply_visitor( + [this, &databases, &setup_error, &stop](auto&& request) -> void { + using ReqType = std::decay_t; + if (setup_error) { + sendResponse(pipe_to_client, ReqType::ResponseType::makeError(*setup_error)); + return; + } + if constexpr (std::is_same_v) { + sendResponse(pipe_to_client, DefaultResponse{}); + } else if constexpr (std::is_same_v) { + logr.info("server was requested to stop"); + sendResponse(pipe_to_client, DefaultResponse{}); + stop = true; + } else if constexpr (std::is_same_v) { + logr.info("received request to batch-create tenants [{}:{}) in database '{}'", + request.id_begin, + request.id_end, + request.cluster_file); + auto err_msg = createTenant( + getOrCreateDatabase(databases, request.cluster_file), request.id_begin, request.id_end); + sendResponse(pipe_to_client, DefaultResponse{ std::move(err_msg) }); + } else if constexpr (std::is_same_v) { + logr.info("received request to batch-delete tenants [{}:{}) in database '{}'", + request.id_begin, + request.id_end, + request.cluster_file); + auto err_msg = deleteTenant( + getOrCreateDatabase(databases, request.cluster_file), request.id_begin, request.id_end); + sendResponse(pipe_to_client, DefaultResponse{ std::move(err_msg) }); + } else if constexpr (std::is_same_v) { + logr.info("received request to fetch tenant IDs [{}:{}) in database '{}'", + request.id_begin, + request.id_end, + request.cluster_file); + sendResponse(pipe_to_client, + fetchTenantIds(getOrCreateDatabase(databases, request.cluster_file), + request.id_begin, + request.id_end)); + } else { + logr.error("unknown request received, typename '{}'", typeid(ReqType).name()); + sendResponse(pipe_to_client, ReqType::ResponseType::makeError("unknown request type")); + } + }, + req); } catch (const std::exception& e) { logr.error("fatal exception: {}", e.what()); return; @@ -161,6 +161,7 @@ void AdminServer::start() { boost::optional AdminServer::createTenant(fdb::Database db, int id_begin, int id_end) { try { auto tx = db.createTransaction(); + auto stopwatch = Stopwatch(StartAtCtor{}); logr.info("create_tenants [{}-{})", id_begin, id_end); while (true) { for (auto id = id_begin; id < id_end; id++) { @@ -180,7 +181,8 @@ boost::optional AdminServer::createTenant(fdb::Database db, int id_ return fmt::format("create_tenants [{}:{}) failed with '{}'", id_begin, id_end, f.error().what()); } } - logr.info("create_tenants [{}-{}) OK", id_begin, id_end); + logr.info("create_tenants [{}-{}) OK ({:.3f}s)", id_begin, id_end, toDoubleSeconds(stopwatch.stop().diff())); + stopwatch.start(); logr.info("blobbify_tenants [{}-{})", id_begin, id_end); for (auto id = id_begin; id < id_end; id++) { while (true) { @@ -204,7 +206,7 @@ boost::optional AdminServer::createTenant(fdb::Database db, int id_ } } } - logr.info("blobbify_tenants [{}-{}) OK", id_begin, id_end); + logr.info("blobbify_tenants [{}-{}) OK ({:.3f}s)", id_begin, id_end, toDoubleSeconds(stopwatch.stop().diff())); return {}; } catch (const std::exception& e) { return std::string(e.what()); @@ -307,12 +309,52 @@ boost::optional AdminServer::deleteTenant(fdb::Database db, int id_ } } -Response AdminServer::request(Request req) { - // should always be invoked from client side (currently just the main process) - assert(server_pid > 0); - assert(logr.isFor(ProcKind::MAIN)); - sendObject(pipe_to_server, std::move(req)); - return receiveObject(pipe_to_client); +TenantIdsResponse AdminServer::fetchTenantIds(fdb::Database db, int id_begin, int id_end) { + try { + logr.info("fetch_tenant_ids [{}:{})", id_begin, id_end); + auto stopwatch = Stopwatch(StartAtCtor{}); + size_t const count = id_end - id_begin; + std::vector ids(count); + std::vector, fdb::Tenant, bool>> state(count); + boost::optional err_msg; + for (auto idx = id_begin; idx < id_end; idx++) { + auto& [future, tenant, done] = state[idx - id_begin]; + tenant = db.openTenant(fdb::toBytesRef(getTenantNameByIndex(idx))); + future = tenant.getId(); + done = false; + } + while (true) { + bool has_retries = false; + for (auto idx = id_begin; idx < id_end; idx++) { + auto& [future, tenant, done] = state[idx - id_begin]; + if (!done) { + if (auto err = future.blockUntilReady()) { + return TenantIdsResponse::makeError( + fmt::format("error while waiting for tenant ID of tenant {}: {}", idx, err.what())); + } + if (auto err = future.error()) { + if (err.retryable()) { + logr.debug("retryable error while getting tenant ID of tenant {}: {}", idx, err.what()); + future = tenant.getId(); + has_retries = true; + } else { + return TenantIdsResponse::makeError(fmt::format( + "unretryable error while getting tenant ID of tenant {}: {}", idx, err.what())); + } + } else { + ids[idx - id_begin] = future.get(); + done = true; + } + } + } + if (!has_retries) + break; + } + logr.info("fetch_tenant_ids [{}:{}) OK ({:.3f}s)", id_begin, id_end, toDoubleSeconds(stopwatch.stop().diff())); + return TenantIdsResponse{ {}, std::move(ids) }; + } catch (const std::exception& e) { + return TenantIdsResponse::makeError(fmt::format("unexpected exception: {}", e.what())); + } } AdminServer::~AdminServer() { diff --git a/bindings/c/test/mako/admin_server.hpp b/bindings/c/test/mako/admin_server.hpp index 4b482fe10d..87a91e52c9 100644 --- a/bindings/c/test/mako/admin_server.hpp +++ b/bindings/c/test/mako/admin_server.hpp @@ -23,6 +23,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include + #include #include "fdb_api.hpp" #include "logger.hpp" @@ -35,16 +42,32 @@ extern thread_local mako::Logger logr; // Therefore, order to benchmark for authorization namespace mako::ipc { -struct Response { +struct DefaultResponse { boost::optional error_message; + static DefaultResponse makeError(std::string msg) { return DefaultResponse{ msg }; } + template void serialize(Ar& ar, unsigned int) { ar& error_message; } }; +struct TenantIdsResponse { + boost::optional error_message; + std::vector ids; + + static TenantIdsResponse makeError(std::string msg) { return TenantIdsResponse{ msg, {} }; } + + template + void serialize(Ar& ar, unsigned int) { + ar& error_message; + ar& ids; + } +}; + struct BatchCreateTenantRequest { + using ResponseType = DefaultResponse; std::string cluster_file; int id_begin = 0; int id_end = 0; @@ -58,6 +81,7 @@ struct BatchCreateTenantRequest { }; struct BatchDeleteTenantRequest { + using ResponseType = DefaultResponse; std::string cluster_file; int id_begin = 0; int id_end = 0; @@ -70,17 +94,34 @@ struct BatchDeleteTenantRequest { } }; +struct FetchTenantIdsRequest { + using ResponseType = TenantIdsResponse; + std::string cluster_file; + int id_begin; + int id_end; + + template + void serialize(Ar& ar, unsigned int) { + ar& cluster_file; + ar& id_begin; + ar& id_end; + } +}; + struct PingRequest { + using ResponseType = DefaultResponse; template void serialize(Ar&, unsigned int) {} }; struct StopRequest { + using ResponseType = DefaultResponse; template void serialize(Ar&, unsigned int) {} }; -using Request = boost::variant; +using Request = + boost::variant; class AdminServer { const Arguments& args; @@ -89,13 +130,32 @@ class AdminServer { boost::process::pstream pipe_to_client; void start(); void configure(); - Response request(Request req); boost::optional getTenantPrefixes(fdb::Transaction tx, int id_begin, int id_end, std::vector& out_prefixes); boost::optional createTenant(fdb::Database db, int id_begin, int id_end); boost::optional deleteTenant(fdb::Database db, int id_begin, int id_end); + TenantIdsResponse fetchTenantIds(fdb::Database db, int id_begin, int id_end); + + template + static void sendObject(boost::process::pstream& pipe, T obj) { + boost::archive::binary_oarchive oa(pipe); + oa << obj; + } + + template + static T receiveObject(boost::process::pstream& pipe) { + boost::archive::binary_iarchive ia(pipe); + T obj; + ia >> obj; + return obj; + } + + template + static void sendResponse(boost::process::pstream& pipe, typename RequestType::ResponseType obj) { + sendObject(pipe, std::move(obj)); + } public: AdminServer(const Arguments& args) @@ -107,9 +167,14 @@ public: // forks a server subprocess internally bool isClient() const noexcept { return server_pid > 0; } + template - Response send(T req) { - return request(Request(std::forward(req))); + typename T::ResponseType send(T req) { + // should always be invoked from client side (currently just the main process) + assert(server_pid > 0); + assert(logr.isFor(ProcKind::MAIN)); + sendObject(pipe_to_server, Request(std::move(req))); + return receiveObject(pipe_to_client); } AdminServer(const AdminServer&) = delete; diff --git a/bindings/c/test/mako/mako.cpp b/bindings/c/test/mako/mako.cpp index bce5709fc1..d4e858b08a 100644 --- a/bindings/c/test/mako/mako.cpp +++ b/bindings/c/test/mako/mako.cpp @@ -90,10 +90,11 @@ using namespace mako; thread_local Logger logr = Logger(MainProcess{}, VERBOSE_DEFAULT); -Transaction createNewTransaction(Database db, Arguments const& args, int id = -1, Tenant* tenants = nullptr) { +std::pair /*token*/> +createNewTransaction(Database db, Arguments const& args, int id, std::optional>& tenants) { // No tenants specified if (args.active_tenants <= 0) { - return db.createTransaction(); + return { db.createTransaction(), {} }; } // Create Tenant Transaction int tenant_id = (id == -1) ? urand(0, args.active_tenants - 1) : id; @@ -101,7 +102,7 @@ Transaction createNewTransaction(Database db, Arguments const& args, int id = -1 std::string tenant_name; // If provided tenants array, use it if (tenants) { - tr = tenants[tenant_id].createTransaction(); + tr = (*tenants)[tenant_id].createTransaction(); } else { tenant_name = getTenantNameByIndex(tenant_id); Tenant t = db.openTenant(toBytesRef(tenant_name)); @@ -120,7 +121,7 @@ Transaction createNewTransaction(Database db, Arguments const& args, int id = -1 _exit(1); } } - return tr; + return { tr, { tenant_name } }; } int cleanupTenants(ipc::AdminServer& server, Arguments const& args, int db_id) { @@ -197,14 +198,11 @@ int populate(Database db, const ThreadArgs& thread_args, int thread_tps, Workflo auto watch_trace = Stopwatch(watch_total.getStart()); // tenants are assumed to have been generated by populateTenants() at main process, pre-fork - Tenant tenants[args.active_tenants]; - for (int i = 0; i < args.active_tenants; ++i) { - tenants[i] = db.openTenant(toBytesRef(getTenantNameByIndex(i))); - } + std::optional> tenants = args.prepareTenants(db); int populate_iters = args.active_tenants > 0 ? args.active_tenants : 1; // Each tenant should have the same range populated for (auto t_id = 0; t_id < populate_iters; ++t_id) { - Transaction tx = createNewTransaction(db, args, t_id, args.active_tenants > 0 ? tenants : nullptr); + auto [tx, token] = createNewTransaction(db, args, t_id, tenants); const auto key_begin = insertBegin(args.rows, process_idx, thread_idx, args.num_processes, args.num_threads); const auto key_end = insertEnd(args.rows, process_idx, thread_idx, args.num_processes, args.num_threads); auto key_checkpoint = key_begin; // in case of commit failure, restart from this key @@ -262,7 +260,7 @@ int populate(Database db, const ThreadArgs& thread_args, int thread_tps, Workflo auto tx_restarter = ExitGuard([&watch_tx]() { watch_tx.startFromStop(); }); if (rc == FutureRC::OK) { key_checkpoint = i + 1; // restart on failures from next key - tx = createNewTransaction(db, args, t_id, args.active_tenants > 0 ? tenants : nullptr); + std::tie(tx, token) = createNewTransaction(db, args, t_id, tenants); } else if (rc == FutureRC::ABORT) { return -1; } else { @@ -306,6 +304,7 @@ void updateErrorStatsRunMode(WorkflowStatistics& stats, fdb::Error err, int op) /* run one iteration of configured transaction */ int runOneTransaction(Transaction& tx, + std::optional const& token, Arguments const& args, WorkflowStatistics& stats, ByteString& key1, @@ -356,6 +355,8 @@ transaction_begin: stats.addLatency(OP_COMMIT, step_latency); } tx.reset(); + if (token) + tx.setOption(FDB_TR_OPTION_AUTHORIZATION_TOKEN, *token); stats.incrOpCount(OP_COMMIT); needs_commit = false; } @@ -444,12 +445,7 @@ int runWorkload(Database db, auto val = ByteString{}; val.resize(args.value_length); - // mimic typical tenant usage: keep tenants in memory - // and create transactions as needed - Tenant tenants[args.active_tenants]; - for (int i = 0; i < args.active_tenants; ++i) { - tenants[i] = db.openTenant(toBytesRef(getTenantNameByIndex(i))); - } + std::optional> tenants = args.prepareTenants(db); /* main transaction loop */ while (1) { @@ -470,7 +466,7 @@ int runWorkload(Database db, } if (current_tps > 0 || thread_tps == 0 /* throttling off */) { - Transaction tx = createNewTransaction(db, args, -1, args.active_tenants > 0 ? tenants : nullptr); + auto [tx, token] = createNewTransaction(db, args, -1, tenants); setTransactionTimeoutIfEnabled(args, tx); /* enable transaction trace */ @@ -507,7 +503,7 @@ int runWorkload(Database db, } } - rc = runOneTransaction(tx, args, workflow_stats, key1, key2, val); + rc = runOneTransaction(tx, token, args, workflow_stats, key1, key2, val); if (rc) { logr.warn("runOneTransaction failed ({})", rc); } @@ -580,7 +576,7 @@ void runAsyncWorkload(Arguments const& args, auto state = std::make_shared(Logger(WorkerProcess{}, args.verbose, process_idx, i), db, - createNewTransaction(db, args), + db.createTransaction(), io_context, args, shm.workerStatsSlot(process_idx, i), @@ -613,7 +609,7 @@ void runAsyncWorkload(Arguments const& args, auto state = std::make_shared(Logger(WorkerProcess{}, args.verbose, process_idx, i), db, - createNewTransaction(db, args), + db.createTransaction(), io_context, args, shm.workerStatsSlot(process_idx, i), @@ -1685,6 +1681,10 @@ int Arguments::validate() { } if (enable_token_based_authorization) { + if (num_fdb_clusters > 1) { + logr.error("for simplicity, --enable_token_based_authorization must be used with exactly one fdb cluster"); + return -1; + } if (active_tenants <= 0 || total_tenants <= 0) { logr.error("--enable_token_based_authorization must be used with at least one tenant"); return -1; @@ -1708,18 +1708,40 @@ bool Arguments::isAuthorizationEnabled() const noexcept { private_key_pem.has_value(); } +void Arguments::collectTenantIds() { + auto db = Database(cluster_files[0]); + tenant_ids.clear(); + tenant_ids.reserve(active_tenants); +} + void Arguments::generateAuthorizationTokens() { assert(active_tenants > 0); assert(keypair_id.has_value()); assert(private_key_pem.has_value()); authorization_tokens.clear(); + assert(num_fdb_clusters == 1); + assert(!tenant_ids.empty()); + // assumes tenants have already been populated logr.info("generating authorization tokens to be used by worker threads"); auto stopwatch = Stopwatch(StartAtCtor{}); - authorization_tokens = generateAuthorizationTokenMap(active_tenants, keypair_id.value(), private_key_pem.value()); + authorization_tokens = + generateAuthorizationTokenMap(active_tenants, keypair_id.value(), private_key_pem.value(), tenant_ids); assert(authorization_tokens.size() == active_tenants); logr.info("generated {} tokens in {:6.3f} seconds", active_tenants, toDoubleSeconds(stopwatch.stop().diff())); } +std::optional> Arguments::prepareTenants(fdb::Database db) const { + if (active_tenants > 0) { + std::vector tenants(active_tenants); + for (auto i = 0; i < active_tenants; i++) { + tenants[i] = db.openTenant(toBytesRef(getTenantNameByIndex(i))); + } + return tenants; + } else { + return {}; + } +} + void printStats(Arguments const& args, WorkflowStatistics const* stats, double const duration_sec, FILE* fp) { static WorkflowStatistics prev; @@ -2503,10 +2525,6 @@ int main(int argc, char* argv[]) { logr.setVerbosity(args.verbose); - if (args.isAuthorizationEnabled()) { - args.generateAuthorizationTokens(); - } - if (args.mode == MODE_CLEAN) { /* cleanup will be done from a single thread */ args.num_processes = 1; @@ -2525,7 +2543,8 @@ int main(int argc, char* argv[]) { return 0; } - if (args.total_tenants > 0 && (args.mode == MODE_BUILD || args.mode == MODE_CLEAN)) { + if (args.total_tenants > 0 && + (args.isAuthorizationEnabled() || args.mode == MODE_BUILD || args.mode == MODE_CLEAN)) { // below construction fork()s internally auto server = ipc::AdminServer(args); @@ -2542,12 +2561,11 @@ int main(int argc, char* argv[]) { logr.info("admin server ready"); } } - // Use admin server to request tenant creation or deletion. - // This is necessary when tenant authorization is enabled, - // in which case the worker threads connect to database as untrusted clients, - // as which they wouldn't be allowed to create/delete tenants on their own. - // Although it is possible to allow worker threads to create/delete - // tenants in a authorization-disabled mode, use the admin server anyway for simplicity. + // Use admin server as proxy to creating/deleting tenants or pre-fetching tenant IDs for token signing when + // authorization is enabled This is necessary when tenant authorization is enabled, in which case the worker + // threads connect to database as untrusted clients, as which they wouldn't be allowed to create/delete tenants + // on their own. Although it is possible to allow worker threads to create/delete tenants in a + // authorization-disabled mode, use the admin server anyway for simplicity. if (args.mode == MODE_CLEAN) { // short-circuit tenant cleanup const auto num_dbs = std::min(args.num_fdb_clusters, args.num_databases); @@ -2563,6 +2581,21 @@ int main(int argc, char* argv[]) { return -1; } } + if ((args.mode == MODE_BUILD || args.mode == MODE_RUN) && args.isAuthorizationEnabled()) { + assert(args.num_fdb_clusters == 1); + // need to fetch tenant IDs to pre-generate tokens + // fetch all IDs in one go + auto res = server.send(ipc::FetchTenantIdsRequest{ args.cluster_files[0], 0, args.active_tenants }); + if (res.error_message) { + logr.error("tenant ID fetch failed: {}", *res.error_message); + return -1; + } else { + logr.info("Successfully prefetched {} tenant IDs", res.ids.size()); + assert(res.ids.size() == args.active_tenants); + args.tenant_ids = std::move(res.ids); + } + args.generateAuthorizationTokens(); + } } const auto pid_main = getpid(); diff --git a/bindings/c/test/mako/mako.hpp b/bindings/c/test/mako/mako.hpp index f442e88fa8..0109572d46 100644 --- a/bindings/c/test/mako/mako.hpp +++ b/bindings/c/test/mako/mako.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -143,10 +144,13 @@ constexpr const int MAX_REPORT_FILES = 200; struct Arguments { Arguments(); int validate(); + void collectTenantIds(); bool isAuthorizationEnabled() const noexcept; + std::optional> prepareTenants(fdb::Database db) const; void generateAuthorizationTokens(); - // Needs to be called once per fdb-accessing process + // Needs to be called once per fdb client process from a clean state: + // i.e. no FDB API called int setGlobalOptions() const; bool isAnyTimeoutEnabled() const; @@ -206,6 +210,7 @@ struct Arguments { std::optional keypair_id; std::optional private_key_pem; std::map authorization_tokens; // maps tenant name to token string + std::vector tenant_ids; // maps tenant index to tenant id for signing tokens int transaction_timeout_db; int transaction_timeout_tx; }; diff --git a/bindings/c/test/mako/tenant.cpp b/bindings/c/test/mako/tenant.cpp index 96d66aec2e..8285c6f03a 100644 --- a/bindings/c/test/mako/tenant.cpp +++ b/bindings/c/test/mako/tenant.cpp @@ -28,7 +28,8 @@ namespace mako { std::map generateAuthorizationTokenMap(int num_tenants, std::string public_key_id, - std::string private_key_pem) { + std::string private_key_pem, + const std::vector& tenant_ids) { std::map m; auto t = authz::jwt::stdtypes::TokenSpec{}; auto const now = toIntegerSeconds(std::chrono::system_clock::now().time_since_epoch()); @@ -40,14 +41,14 @@ std::map generateAuthorizationTokenMap(int num_tenants t.issuedAtUnixTime = now; t.expiresAtUnixTime = now + 60 * 60 * 12; // Good for 12 hours t.notBeforeUnixTime = now - 60 * 5; // activated 5 mins ago - const int tokenIdLen = 36; // UUID length - auto tokenId = std::string(tokenIdLen, '\0'); + const int tokenid_len = 36; // UUID length + auto tokenid = std::string(tokenid_len, '\0'); for (auto i = 0; i < num_tenants; i++) { std::string tenant_name = getTenantNameByIndex(i); // swap out only the token ids and tenant names - randomAlphanumString(tokenId.data(), tokenIdLen); - t.tokenId = tokenId; - t.tenants = std::vector{ tenant_name }; + randomAlphanumString(tokenid.data(), tokenid_len); + t.tokenId = tokenid; + t.tenants = std::vector{ tenant_ids[i] }; m[tenant_name] = authz::jwt::stdtypes::signToken(t, private_key_pem); } return m; diff --git a/bindings/c/test/mako/tenant.hpp b/bindings/c/test/mako/tenant.hpp index ae038d9397..988ada45ff 100644 --- a/bindings/c/test/mako/tenant.hpp +++ b/bindings/c/test/mako/tenant.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "fdb_api.hpp" #include "utils.hpp" @@ -28,7 +29,8 @@ namespace mako { std::map generateAuthorizationTokenMap(int tenants, std::string public_key_id, - std::string private_key_pem); + std::string private_key_pem, + const std::vector& tenant_ids); inline std::string getTenantNameByIndex(int index) { assert(index >= 0); diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 91f26dad07..9d6adfc447 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -3312,7 +3312,6 @@ Reference TransactionState::cloneAndReset(ReferencestartTime = startTime; newState->committedVersion = committedVersion; newState->conflictingKeys = conflictingKeys; - newState->authToken = authToken; newState->tenantSet = tenantSet; return newState; diff --git a/fdbclient/include/fdbclient/CommitProxyInterface.h b/fdbclient/include/fdbclient/CommitProxyInterface.h index c59a8b71b7..abb26f2e99 100644 --- a/fdbclient/include/fdbclient/CommitProxyInterface.h +++ b/fdbclient/include/fdbclient/CommitProxyInterface.h @@ -684,6 +684,8 @@ struct GlobalConfigRefreshRequest { GlobalConfigRefreshRequest() {} explicit GlobalConfigRefreshRequest(Version lastKnown) : lastKnown(lastKnown) {} + bool verify() const noexcept { return true; } + template void serialize(Ar& ar) { serializer(ar, lastKnown, reply); diff --git a/fdbclient/include/fdbclient/CoordinationInterface.h b/fdbclient/include/fdbclient/CoordinationInterface.h index 679f2a8363..bbbc331f22 100644 --- a/fdbclient/include/fdbclient/CoordinationInterface.h +++ b/fdbclient/include/fdbclient/CoordinationInterface.h @@ -317,6 +317,9 @@ struct ProtocolInfoRequest { constexpr static FileIdentifier file_identifier = 13261233; ReplyPromise reply{ PeerCompatibilityPolicy{ RequirePeer::AtLeast, ProtocolVersion::withStableInterfaces() } }; + + bool verify() const noexcept { return true; } + template void serialize(Ar& ar) { serializer(ar, reply); diff --git a/fdbclient/include/fdbclient/GrvProxyInterface.h b/fdbclient/include/fdbclient/GrvProxyInterface.h index 5d1cec15b2..0209ad4dac 100644 --- a/fdbclient/include/fdbclient/GrvProxyInterface.h +++ b/fdbclient/include/fdbclient/GrvProxyInterface.h @@ -43,7 +43,7 @@ struct GrvProxyInterface { // committed) RequestStream> waitFailure; // reports heartbeat to master. RequestStream getHealthMetrics; - RequestStream refreshGlobalConfig; + PublicRequestStream refreshGlobalConfig; UID id() const { return getConsistentReadVersion.getEndpoint().token; } std::string toString() const { return id().shortString(); } @@ -60,7 +60,7 @@ struct GrvProxyInterface { RequestStream>(getConsistentReadVersion.getEndpoint().getAdjustedEndpoint(1)); getHealthMetrics = RequestStream( getConsistentReadVersion.getEndpoint().getAdjustedEndpoint(2)); - refreshGlobalConfig = RequestStream( + refreshGlobalConfig = PublicRequestStream( getConsistentReadVersion.getEndpoint().getAdjustedEndpoint(3)); } } diff --git a/fdbclient/vexillographer/fdb.options b/fdbclient/vexillographer/fdb.options index 824ad8e015..1ca366b518 100644 --- a/fdbclient/vexillographer/fdb.options +++ b/fdbclient/vexillographer/fdb.options @@ -331,7 +331,8 @@ description is not currently required but encouraged. hidden="true"/>