mirror of
https://github.com/apple/foundationdb.git
synced 2025-05-13 17:39:31 +08:00
Add fdbcli test for tenants. Add documentation for new fdbcli tenant commands. Various output cleanup. Fix limit parsing bug in listtenants command. Update gettenant output format.
This commit is contained in:
parent
bd64781ad9
commit
a23add6bc4
@ -542,6 +542,121 @@ def triggerddteaminfolog(logger):
|
||||
output = run_fdbcli_command('triggerddteaminfolog')
|
||||
assert output == 'Triggered team info logging in data distribution.'
|
||||
|
||||
@enable_logging()
|
||||
def tenants(logger):
|
||||
output = run_fdbcli_command('listtenants')
|
||||
print(output)
|
||||
assert output == 'The cluster has no tenants'
|
||||
|
||||
output = run_fdbcli_command('createtenant tenant')
|
||||
print(output)
|
||||
assert output == 'The tenant `tenant\' has been created'
|
||||
|
||||
output = run_fdbcli_command('createtenant tenant2')
|
||||
print(output)
|
||||
assert output == 'The tenant `tenant2\' has been created'
|
||||
|
||||
output = run_fdbcli_command('listtenants')
|
||||
print(output)
|
||||
assert output == '1. tenant\n 2. tenant2'
|
||||
|
||||
output = run_fdbcli_command('listtenants a z 1')
|
||||
print(output)
|
||||
assert output == '1. tenant'
|
||||
|
||||
output = run_fdbcli_command('listtenants a tenant2')
|
||||
print(output)
|
||||
assert output == '1. tenant'
|
||||
|
||||
output = run_fdbcli_command('listtenants tenant2 z')
|
||||
print(output)
|
||||
assert output == '1. tenant2'
|
||||
|
||||
output = run_fdbcli_command('gettenant tenant')
|
||||
print(output)
|
||||
lines = output.split('\n')
|
||||
assert len(lines) == 2
|
||||
assert lines[0].strip().startswith('id: ')
|
||||
assert lines[1].strip().startswith('prefix: ')
|
||||
|
||||
output = run_fdbcli_command('usetenant')
|
||||
print(output)
|
||||
assert output == 'Using the default tenant'
|
||||
|
||||
output = run_fdbcli_command_and_get_error('usetenant tenant3')
|
||||
print(output)
|
||||
assert output == 'ERROR: Tenant `tenant3\' does not exist'
|
||||
|
||||
# Test writing keys to different tenants and make sure they all work correctly
|
||||
run_fdbcli_command('writemode on; set tenant_test default_tenant')
|
||||
output = run_fdbcli_command('get tenant_test')
|
||||
print(output)
|
||||
assert output == '`tenant_test\' is `default_tenant\''
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['writemode on', 'usetenant tenant', 'get tenant_test', 'set tenant_test tenant']
|
||||
output, _ = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-3:]
|
||||
print(lines)
|
||||
assert lines[0] == 'Using tenant `tenant\''
|
||||
assert lines[1] == '`tenant_test\': not found'
|
||||
assert lines[2].startswith('Committed')
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['writemode on', 'usetenant tenant2', 'get tenant_test', 'set tenant_test tenant2', 'get tenant_test']
|
||||
output, _ = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-4:]
|
||||
print(lines)
|
||||
assert lines[0] == 'Using tenant `tenant2\''
|
||||
assert lines[1] == '`tenant_test\': not found'
|
||||
assert lines[2].startswith('Committed')
|
||||
assert lines[3] == '`tenant_test\' is `tenant2\''
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['usetenant tenant', 'get tenant_test', 'defaulttenant', 'get tenant_test']
|
||||
output, _ = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-4:]
|
||||
print(lines)
|
||||
assert lines[0] == 'Using tenant `tenant\''
|
||||
assert lines[1] == '`tenant_test\' is `tenant\''
|
||||
assert lines[2] == 'Using the default tenant'
|
||||
assert lines[3] == '`tenant_test\' is `default_tenant\''
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['writemode on', 'usetenant tenant', 'clear tenant_test', 'deletetenant tenant', 'get tenant_test', 'defaulttenant', 'usetenant tenant']
|
||||
output, error_output = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-7:]
|
||||
error_lines = error_output.decode().strip().split('\n')[-2:]
|
||||
print(lines)
|
||||
print(error_lines)
|
||||
assert lines[0] == 'Using tenant `tenant\''
|
||||
assert lines[1].startswith('Committed')
|
||||
assert lines[2] == 'The tenant `tenant\' has been deleted'
|
||||
assert lines[3] == 'WARNING: the active tenant was deleted. Use the `usetenant\' or `defaulttenant\''
|
||||
assert lines[4] == 'command to choose a new tenant.'
|
||||
assert error_lines[0] == 'ERROR: Tenant does not exist (2131)'
|
||||
assert lines[6] == 'Using the default tenant'
|
||||
assert error_lines[1] == 'ERROR: Tenant `tenant\' does not exist'
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['writemode on', 'deletetenant tenant2', 'usetenant tenant2', 'clear tenant_test', 'defaulttenant', 'deletetenant tenant2']
|
||||
output, error_output = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-4:]
|
||||
error_lines = error_output.decode().strip().split('\n')[-1:]
|
||||
print(lines)
|
||||
print(error_lines)
|
||||
assert error_lines[0] == 'ERROR: Cannot delete a non-empty tenant (2133)'
|
||||
assert lines[0] == 'Using tenant `tenant2\''
|
||||
assert lines[1].startswith('Committed')
|
||||
assert lines[2] == 'Using the default tenant'
|
||||
assert lines[3] == 'The tenant `tenant2\' has been deleted'
|
||||
|
||||
run_fdbcli_command('writemode on; clear tenant_test')
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter,
|
||||
@ -586,6 +701,7 @@ if __name__ == '__main__':
|
||||
transaction()
|
||||
throttle()
|
||||
triggerddteaminfolog()
|
||||
tenants()
|
||||
else:
|
||||
assert args.process_number > 1, "Process number should be positive"
|
||||
coordinators()
|
||||
|
@ -153,6 +153,27 @@ If ``description=<DESC>`` is specified, the description field in the cluster fil
|
||||
|
||||
For more information on setting the cluster description, see :ref:`configuration-setting-cluster-description`.
|
||||
|
||||
createtenant
|
||||
------------
|
||||
|
||||
The ``createtenant`` command is used to create new tenants in the cluster. Its syntax is ``createtenant <TENANT_NAME>``.
|
||||
|
||||
The tenant name can be any byte string that does not begin with the ``\xff`` byte. If the tenant already exists, ``fdbcli`` will report an error.
|
||||
|
||||
defaulttenant
|
||||
-------------
|
||||
|
||||
The ``defaulttenant`` command configures ``fdbcli`` to run its commands without a tenant. This is the default behavior.
|
||||
|
||||
The active tenant cannot be changed while a transaction (using ``begin``) is open.
|
||||
|
||||
deletetenant
|
||||
------------
|
||||
|
||||
The ``deletetenant`` command is used to delete tenants from the cluster. Its syntax is ``deletetenant <TENANT_NAME>``.
|
||||
|
||||
In order to delete a tenant, it must be empty. To delete a tenant with data, first clear that data using the ``clear`` command. If the tenant does not exist, ``fdbcli`` will report an error.
|
||||
|
||||
exclude
|
||||
-------
|
||||
|
||||
@ -210,6 +231,13 @@ The ``getrangekeys`` command fetches keys in a range. Its syntax is ``getrangeke
|
||||
|
||||
Note that :ref:`characters can be escaped <cli-escaping>` when specifying keys (or values) in ``fdbcli``.
|
||||
|
||||
gettenant
|
||||
---------
|
||||
|
||||
The ``gettenant`` command fetches metadata for a given tenant and displays it. Its syntax is ``gettenant <TENANT_NAME>``.
|
||||
|
||||
Included in the output of this command are the ``id`` and ``prefix`` assigned to the tenant. If the tenant does not exist, ``fdbcli`` will report an error.
|
||||
|
||||
getversion
|
||||
----------
|
||||
|
||||
@ -300,6 +328,13 @@ Attempts to kill all specified processes. Each address should include the IP and
|
||||
|
||||
Attempts to kill all known processes in the cluster.
|
||||
|
||||
listtenants
|
||||
-----------
|
||||
|
||||
The ``listtenants`` command prints the names of tenants in the cluster. Its syntax is ``listtenants [BEGIN] [END] [LIMIT]``.
|
||||
|
||||
By default, the ``listtenants`` command will print up to 100 entries from the entire range of tenants. A narrower sub-range can be printed using the optional ``[BEGIN]`` and ``[END]`` parameters, and the limit can be changed by specifying an integer ``[LIMIT]`` parameter.
|
||||
|
||||
lock
|
||||
----
|
||||
|
||||
@ -512,6 +547,17 @@ unlock
|
||||
|
||||
The ``unlock`` command unlocks the database with the specified lock UID. Because this is a potentially dangerous operation, users must copy a passphrase before the unlock command is executed.
|
||||
|
||||
usetenant
|
||||
---------
|
||||
|
||||
The ``usetenant`` command configures ``fdbcli`` to run transactions within the specified tenant. Its syntax is ``usetenant <TENANT_NAME>``.
|
||||
|
||||
When configured, transactions will read and write keys from the key-space associated with the specified tenant. By default, ``fdbcli`` runs without a tenant. Management operations that modify keys (e.g. ``exclude``) will not operate within the tenant.
|
||||
|
||||
If the tenant chosen does not exist, ``fdbcli`` will report an error.
|
||||
|
||||
The active tenant cannot be changed while a transaction (using ``begin``) is open.
|
||||
|
||||
writemode
|
||||
---------
|
||||
|
||||
|
@ -72,7 +72,7 @@ ACTOR Future<bool> createTenantCommandActor(Reference<IDatabase> db, std::vector
|
||||
}
|
||||
}
|
||||
|
||||
printf("The tenant `%s' has been created.\n", printable(tokens[1]).c_str());
|
||||
printf("The tenant `%s' has been created\n", printable(tokens[1]).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ ACTOR Future<bool> deleteTenantCommandActor(Reference<IDatabase> db, std::vector
|
||||
}
|
||||
}
|
||||
|
||||
printf("The tenant `%s' has been deleted.\n", printable(tokens[1]).c_str());
|
||||
printf("The tenant `%s' has been deleted\n", printable(tokens[1]).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ ACTOR Future<bool> listTenantsCommandActor(Reference<IDatabase> db, std::vector<
|
||||
}
|
||||
if (tokens.size() == 4) {
|
||||
int n = 0;
|
||||
if (sscanf(tokens[3].toString().c_str(), "%d%n", &limit, &n) != 1 || n != tokens[4].size()) {
|
||||
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;
|
||||
}
|
||||
@ -168,9 +168,9 @@ ACTOR Future<bool> listTenantsCommandActor(Reference<IDatabase> db, std::vector<
|
||||
|
||||
if (tenants.empty()) {
|
||||
if (tokens.size() == 1) {
|
||||
printf("The cluster has no tenants.\n");
|
||||
printf("The cluster has no tenants\n");
|
||||
} else {
|
||||
printf("The cluster has no tenants in the specified range.\n");
|
||||
printf("The cluster has no tenants in the specified range\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +218,17 @@ ACTOR Future<bool> getTenantCommandActor(Reference<IDatabase> db, std::vector<St
|
||||
throw tenant_not_found();
|
||||
}
|
||||
|
||||
printf(" %s\n", tenant.get().toString().c_str());
|
||||
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);
|
||||
|
@ -1995,6 +1995,11 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
||||
bool _result = wait(makeInterruptable(deleteTenantCommandActor(db, tokens)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
else if (tenantName.present() && tokens[1] == tenantName.get()) {
|
||||
printAtCol("WARNING: the active tenant was deleted. Use the `usetenant' or `defaulttenant' "
|
||||
"command to choose a new tenant.\n",
|
||||
80);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -136,5 +136,5 @@ logdir = {logdir}
|
||||
|
||||
def create_database(self, storage='ssd'):
|
||||
args = [self.fdbcli_binary, '-C', self.etc.joinpath('fdb.cluster'), '--exec',
|
||||
'configure new single {}'.format(storage)]
|
||||
'configure new single {} tenant_mode=optional_experimental'.format(storage)]
|
||||
subprocess.run(args)
|
||||
|
Loading…
x
Reference in New Issue
Block a user