From 6146f70bb8a3df3fc5f18eb70e865960b3c6e4c0 Mon Sep 17 00:00:00 2001 From: adal-chiriliuc-reef <165330877+adal-chiriliuc-reef@users.noreply.github.com> Date: Wed, 24 Apr 2024 18:46:30 +0300 Subject: [PATCH] Added key, replication, account subcommands (#1020) * added key {list|create|delete} commands * updated tests and docs to use new key subcommands * nox -s format * added changelog.d files * fixed changelog file * updated key example * added tests for deprecated key commands * nox -s format * fixed deprecated commands tests * fixed deprecated command integration tests * fixed deprecated integration tests * added Subcommand suffix * added replication subcommands * combine changelog.d files. * updated tests to use both commands and subcommands * fixed deprecated integration tests * renamed changelog files * added account subcommands * updated documentation * use account subcommands in tests * use `account authorize` subcommand in tests * use `account authorize` subcommand in tests * nox -s format * fixed tests * fixed typo * add commands to v3 registry * reverted README.md change * reverted README.md change * fixed tests * added deprecated account commands tests --- README.md | 2 +- b2/_internal/_b2v4/registry.py | 3 + b2/_internal/b2v3/registry.py | 3 + b2/_internal/console_tool.py | 214 ++++++++++++++++-- changelog.d/+command-account.added.md | 1 + changelog.d/+command-account.deprecated.md | 1 + changelog.d/+command-key.added.md | 1 + changelog.d/+command-key.deprecated.md | 1 + changelog.d/+command-replication.added.md | 1 + .../+command-replication.deprecated.md | 1 + doc/source/quick_start.rst | 6 +- doc/source/replication.rst | 4 +- test/integration/helpers.py | 6 +- test/integration/test_b2_command_line.py | 175 ++++++++++---- .../console_tool/test_authorize_account.py | 36 ++- test/unit/test_console_tool.py | 179 +++++++++++---- 16 files changed, 499 insertions(+), 135 deletions(-) create mode 100644 changelog.d/+command-account.added.md create mode 100644 changelog.d/+command-account.deprecated.md create mode 100644 changelog.d/+command-key.added.md create mode 100644 changelog.d/+command-key.deprecated.md create mode 100644 changelog.d/+command-replication.added.md create mode 100644 changelog.d/+command-replication.deprecated.md diff --git a/README.md b/README.md index 6a5eed857..c9ae59549 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ B2_APPLICATION_KEY= B2_APPLICATION_KEY_ID= docker run --rm -e B2_AP or authorize once and keep the credentials persisted: ```bash -docker run --rm -it -v b2:/root backblazeit/b2:latest b2v3 authorize-account +docker run --rm -it -v b2:/root backblazeit/b2:latest b2v3 account authorize docker run --rm -v b2:/root backblazeit/b2:latest b2v3 list-buckets # remember to include `-v` - authorization details are there ``` diff --git a/b2/_internal/_b2v4/registry.py b/b2/_internal/_b2v4/registry.py index 36dabfaaa..2fc9ec644 100644 --- a/b2/_internal/_b2v4/registry.py +++ b/b2/_internal/_b2v4/registry.py @@ -56,3 +56,6 @@ B2.register_subcommand(License) B2.register_subcommand(InstallAutocomplete) B2.register_subcommand(NotificationRules) +B2.register_subcommand(Key) +B2.register_subcommand(Replication) +B2.register_subcommand(Account) diff --git a/b2/_internal/b2v3/registry.py b/b2/_internal/b2v3/registry.py index 665ec1e1e..ff8a98edd 100644 --- a/b2/_internal/b2v3/registry.py +++ b/b2/_internal/b2v3/registry.py @@ -136,3 +136,6 @@ class Ls(B2URIBucketNFolderNameArgMixin, BaseLs): B2.register_subcommand(License) B2.register_subcommand(InstallAutocomplete) B2.register_subcommand(NotificationRules) +B2.register_subcommand(Key) +B2.register_subcommand(Replication) +B2.register_subcommand(Account) diff --git a/b2/_internal/console_tool.py b/b2/_internal/console_tool.py index b3d6cc57e..fedfac232 100644 --- a/b2/_internal/console_tool.py +++ b/b2/_internal/console_tool.py @@ -1145,7 +1145,7 @@ class B2(Command): There are two flows of authorization: - * call ``{NAME} authorize-account`` and have the credentials cached in sqlite + * call ``{NAME} account authorize`` and have the credentials cached in sqlite * set ``{B2_APPLICATION_KEY_ID_ENV_VAR}`` and ``{B2_APPLICATION_KEY_ENV_VAR}`` environment variables when running this program @@ -1166,7 +1166,7 @@ class B2(Command): If the directory ``{XDG_CONFIG_HOME_ENV_VAR}/b2`` does not exist (and is needed), it is created. Please note that the above rules may be changed in next versions of b2sdk, and in order to get - reliable authentication file location you should use ``b2 get-account-info``. + reliable authentication file location you should use ``b2 account get``. Control characters escaping is turned on if running under terminal. You can override it by explicitly using `--escape-control-chars`/`--no-escape-control-chars`` option, @@ -1206,7 +1206,7 @@ def _run(self, args): return args.command_class -class AuthorizeAccount(Command): +class AccountAuthorizeBase(Command): """ Prompts for Backblaze ``applicationKeyId`` and ``applicationKey`` (unless they are given on the command line). @@ -1218,7 +1218,7 @@ class AuthorizeAccount(Command): application key from the ``B2 Cloud Storage Buckets`` page on the web site: https://secure.backblaze.com/b2_buckets.htm - To use a normal application key, created with the ``create-key`` + To use a normal application key, created with the ``key create`` command or on the web site, provide the application key ID and the application key itself. @@ -1383,7 +1383,7 @@ def _run(self, args): return 0 -class ClearAccount(Command): +class AccountClearBase(Command): """ Erases everything in local cache. @@ -1599,7 +1599,7 @@ def _run(self, args): return 0 -class CreateKey(Command): +class KeyCreateBase(Command): """ Creates a new application key. Prints the application key information. This is the only time the application key itself will be returned. Listing application keys will show @@ -1618,7 +1618,7 @@ class CreateKey(Command): The ``namePrefix`` restricts file access to files whose names start with the prefix. The output is the new application key ID, followed by the application key itself. - The two values returned are the two that you pass to ``authorize-account`` to use the key. + The two values returned are the two that you pass to ``account authorize`` to use the key. Requires capability: @@ -1717,7 +1717,7 @@ def _run(self, args): return 0 -class DeleteKey(Command): +class KeyDeleteBase(Command): """ Deletes the specified application key by its ID. @@ -1977,7 +1977,7 @@ def _run(self, args): return 0 -class GetAccountInfo(Command): +class AccountGetBase(Command): """ Shows the account ID, key, auth token, URLs, and what capabilities the current application keys has. @@ -2204,7 +2204,7 @@ def run_list_buckets(cls, command: Command, *, json_: bool) -> int: return 0 -class ListKeys(Command): +class KeyListBase(Command): """ Lists the application keys for the current account. @@ -3653,7 +3653,7 @@ def _run(self, args): return 0 -class ReplicationSetup(Command): +class ReplicationSetupBase(Command): """ Sets up replication between two buckets (potentially from different accounts), creating and replacing keys if necessary. @@ -3779,7 +3779,7 @@ def alter_one_rule(cls, rule: ReplicationRule) -> ReplicationRule | None: pass -class ReplicationDelete(ReplicationRuleChanger): +class ReplicationDeleteBase(ReplicationRuleChanger): """ Deletes a replication rule @@ -3795,7 +3795,7 @@ def alter_one_rule(cls, rule: ReplicationRule) -> ReplicationRule | None: return None -class ReplicationPause(ReplicationRuleChanger): +class ReplicationPauseBase(ReplicationRuleChanger): """ Pauses a replication rule @@ -3812,7 +3812,7 @@ def alter_one_rule(cls, rule: ReplicationRule) -> ReplicationRule | None: return rule -class ReplicationUnpause(ReplicationRuleChanger): +class ReplicationUnpauseBase(ReplicationRuleChanger): """ Unpauses a replication rule @@ -3829,7 +3829,7 @@ def alter_one_rule(cls, rule: ReplicationRule) -> ReplicationRule | None: return rule -class ReplicationStatus(Command): +class ReplicationStatusBase(Command): """ Inspects files in only source or both source and destination buckets (potentially from different accounts) and provides detailed replication statistics. @@ -4633,6 +4633,180 @@ def _run(self, args): return 0 +class Key(Command): + """ + Application keys management subcommands. + + For more information on each subcommand, use ``{NAME} key SUBCOMMAND --help``. + + Examples: + + .. code-block:: + + {NAME} key list + {NAME} key create my-key listFiles,deleteFiles + {NAME} key delete 005c398ac3212400000000010 + """ + subcommands_registry = ClassRegistry(attr_name='COMMAND_NAME') + + +@Key.subcommands_registry.register +class KeyListSubcommand(KeyListBase): + __doc__ = KeyListBase.__doc__ + COMMAND_NAME = 'list' + + +@Key.subcommands_registry.register +class KeyCreateSubcommand(KeyCreateBase): + __doc__ = KeyCreateBase.__doc__ + COMMAND_NAME = 'create' + + +@Key.subcommands_registry.register +class KeyDeleteSubcommand(KeyDeleteBase): + __doc__ = KeyDeleteBase.__doc__ + COMMAND_NAME = 'delete' + + +class ListKeys(CmdReplacedByMixin, KeyListBase): + __doc__ = KeyListBase.__doc__ + replaced_by_cmd = Key + + +class CreateKey(CmdReplacedByMixin, KeyCreateBase): + __doc__ = KeyCreateBase.__doc__ + replaced_by_cmd = Key + + +class DeleteKey(CmdReplacedByMixin, KeyDeleteBase): + __doc__ = KeyDeleteBase.__doc__ + replaced_by_cmd = Key + + +class Replication(Command): + """ + Replication rule management subcommands. + + For more information on each subcommand, use ``{NAME} key SUBCOMMAND --help``. + + Examples: + + .. code-block:: + + {NAME} replication setup --name=my-repl-rule src-bucket dest-bucket + {NAME} replication status --rule=my-repl-rule src-bucket + {NAME} replication pause src-bucket my-repl-rule + {NAME} replication unpause src-bucket my-repl-rule + {NAME} replication delete src-bucket my-repl-rule + """ + subcommands_registry = ClassRegistry(attr_name='COMMAND_NAME') + + +@Replication.subcommands_registry.register +class ReplicationSetupSubcommand(ReplicationSetupBase): + __doc__ = ReplicationSetupBase.__doc__ + COMMAND_NAME = 'setup' + + +@Replication.subcommands_registry.register +class ReplicationStatusSubcommand(ReplicationStatusBase): + __doc__ = ReplicationStatusBase.__doc__ + COMMAND_NAME = 'status' + + +@Replication.subcommands_registry.register +class ReplicationPauseSubcommand(ReplicationPauseBase): + __doc__ = ReplicationPauseBase.__doc__ + COMMAND_NAME = 'pause' + + +@Replication.subcommands_registry.register +class ReplicationUnpauseSubcommand(ReplicationUnpauseBase): + __doc__ = ReplicationUnpauseBase.__doc__ + COMMAND_NAME = 'unpause' + + +@Replication.subcommands_registry.register +class ReplicationDeleteSubcommand(ReplicationDeleteBase): + __doc__ = ReplicationDeleteBase.__doc__ + COMMAND_NAME = 'delete' + + +class ReplicationSetup(CmdReplacedByMixin, ReplicationSetupBase): + __doc__ = ReplicationSetupBase.__doc__ + replaced_by_cmd = Replication + + +class ReplicationStatus(CmdReplacedByMixin, ReplicationStatusBase): + __doc__ = ReplicationStatusBase.__doc__ + replaced_by_cmd = Replication + + +class ReplicationPause(CmdReplacedByMixin, ReplicationPauseBase): + __doc__ = ReplicationPauseBase.__doc__ + replaced_by_cmd = Replication + + +class ReplicationUnpause(CmdReplacedByMixin, ReplicationUnpauseBase): + __doc__ = ReplicationUnpauseBase.__doc__ + replaced_by_cmd = Replication + + +class ReplicationDelete(CmdReplacedByMixin, ReplicationDeleteBase): + __doc__ = ReplicationDeleteBase.__doc__ + replaced_by_cmd = Replication + + +class Account(Command): + """ + Account management subcommands. + + For more information on each subcommand, use ``{NAME} key SUBCOMMAND --help``. + + Examples: + + .. code-block:: + + {NAME} account authorize [applicationKeyId] [applicationKey] + {NAME} account get + {NAME} account clear + """ + subcommands_registry = ClassRegistry(attr_name='COMMAND_NAME') + + +@Account.subcommands_registry.register +class AccountAuthorize(AccountAuthorizeBase): + __doc__ = AccountAuthorizeBase.__doc__ + COMMAND_NAME = 'authorize' + + +@Account.subcommands_registry.register +class AccountGet(AccountGetBase): + __doc__ = AccountGetBase.__doc__ + COMMAND_NAME = 'get' + + +@Account.subcommands_registry.register +class AccountClear(AccountClearBase): + __doc__ = AccountClearBase.__doc__ + COMMAND_NAME = 'clear' + + +class AuthorizeAccount(CmdReplacedByMixin, AccountAuthorizeBase): + __doc__ = AccountAuthorizeBase.__doc__ + replaced_by_cmd = Account + + +class GetAccountInfo(CmdReplacedByMixin, AccountGetBase): + __doc__ = AccountGetBase.__doc__ + replaced_by_cmd = Account + + +class ClearAccount(CmdReplacedByMixin, AccountClearBase): + __doc__ = AccountClearBase.__doc__ + replaced_by_cmd = Account + + class ConsoleTool: """ Implements the commands available in the B2 command-line tool @@ -4702,7 +4876,7 @@ def run_command(self, argv): except MissingAccountData as e: logger.exception('ConsoleTool missing account data error') self._print_stderr( - f'ERROR: {e} Use: {self.b2_binary_name} authorize-account or provide auth data with ' + f'ERROR: {e} Use: \'{self.b2_binary_name} account authorize\' or provide auth data with ' f'{B2_APPLICATION_KEY_ID_ENV_VAR!r} and {B2_APPLICATION_KEY_ENV_VAR!r} environment variables' ) return 1 @@ -4735,8 +4909,8 @@ def _initialize_b2_api(cls, args: argparse.Namespace, kwargs: dict) -> B2Api: except MissingAccountData: is_same_key_on_disk = False - if not is_same_key_on_disk and args.command_class not in ( - AuthorizeAccount, ClearAccount + if not is_same_key_on_disk and not issubclass( + args.command_class, (AccountAuthorizeBase, AccountClearBase) ): # when user specifies keys via env variables, we switch to in-memory account info return _get_inmemory_b2api(**kwargs) @@ -4760,8 +4934,8 @@ def authorize_from_env(self) -> int: if self.api.account_info.is_same_key(key_id, realm or 'production'): return 0 - logger.info('authorize-account is being run from env variables') - return AuthorizeAccount(self).authorize(key_id, key, realm) + logger.info('`account authorize` is being run from env variables') + return AccountAuthorizeBase(self).authorize(key_id, key, realm) def _print(self, *args, **kwargs): print(*args, file=self.stdout, **kwargs) diff --git a/changelog.d/+command-account.added.md b/changelog.d/+command-account.added.md new file mode 100644 index 000000000..cd9b0f1ac --- /dev/null +++ b/changelog.d/+command-account.added.md @@ -0,0 +1 @@ +Add `account {authorize|get|clear}` commands. \ No newline at end of file diff --git a/changelog.d/+command-account.deprecated.md b/changelog.d/+command-account.deprecated.md new file mode 100644 index 000000000..602752fe1 --- /dev/null +++ b/changelog.d/+command-account.deprecated.md @@ -0,0 +1 @@ +Deprecated `authorize-account`, `get-account-info` and `clear-account`, use `account {authorize|get|clear}` instead. \ No newline at end of file diff --git a/changelog.d/+command-key.added.md b/changelog.d/+command-key.added.md new file mode 100644 index 000000000..2bf4c9db1 --- /dev/null +++ b/changelog.d/+command-key.added.md @@ -0,0 +1 @@ +Add `key {list|create|delete}` commands. \ No newline at end of file diff --git a/changelog.d/+command-key.deprecated.md b/changelog.d/+command-key.deprecated.md new file mode 100644 index 000000000..5ea569728 --- /dev/null +++ b/changelog.d/+command-key.deprecated.md @@ -0,0 +1 @@ +Deprecated `list-keys`, `create-key` and `delete-key`, use `key {list|create|delete}` instead. \ No newline at end of file diff --git a/changelog.d/+command-replication.added.md b/changelog.d/+command-replication.added.md new file mode 100644 index 000000000..59c01eb35 --- /dev/null +++ b/changelog.d/+command-replication.added.md @@ -0,0 +1 @@ +Add `replication {setup|delete|pause|unpause|status}` commands. \ No newline at end of file diff --git a/changelog.d/+command-replication.deprecated.md b/changelog.d/+command-replication.deprecated.md new file mode 100644 index 000000000..20b05c9fb --- /dev/null +++ b/changelog.d/+command-replication.deprecated.md @@ -0,0 +1 @@ +Deprecated `replication-{setup|delete|pause|unpause|status}`, use `replication {setup|delete|pause|unpause|status}` instead. \ No newline at end of file diff --git a/doc/source/quick_start.rst b/doc/source/quick_start.rst index 02f299f97..edcc49421 100644 --- a/doc/source/quick_start.rst +++ b/doc/source/quick_start.rst @@ -12,7 +12,7 @@ Prepare B2 cli .. code-block:: sh - $ b2 authorize-account 4ab123456789 001aabbccddeeff123456789012345678901234567 + $ b2 account authorize 4ab123456789 001aabbccddeeff123456789012345678901234567 Using https://api.backblazeb2.com .. tip:: @@ -21,13 +21,13 @@ Prepare B2 cli .. warning:: Local users might be able to access your process list and read command arguments. To avoid exposing credentials, you can provide application key ID and application key using environment variables ``B2_APPLICATION_KEY_ID`` and ``B2_APPLICATION_KEY`` respectively. - Those will be picked up automatically, so after defining those you'll just need to run ``b2 authorize-account`` with no extra parameters. + Those will be picked up automatically, so after defining those you'll just need to run ``b2 account authorize`` with no extra parameters. .. code-block:: sh $ export B2_APPLICATION_KEY_ID="$(