Skip to content

Commit abe127f

Browse files
committed
MB-70102 Add subcommand for modifying the repo cloud config
Add a 'repo-cloud-config' subcommand to modify the cloud options for and existing repository. Currently supports only the --retention-delete-versions flag. Change-Id: Ia1edffa17b9522070e3d7eafa88ed2fe748feee7 Reviewed-on: https://review.couchbase.org/c/couchbase-cli/+/238416 Reviewed-by: Safian Ali <safian.ali@couchbase.com> Tested-by: Build Bot <build@couchbase.com>
1 parent 29f459c commit abe127f

7 files changed

Lines changed: 192 additions & 1 deletion

File tree

cbmgr.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7004,12 +7004,13 @@ def __init__(self):
70047004
self.repo_pause_cmd = BackupServiceRepoPause(self.subparser)
70057005
self.repo_resume_cmd = BackupServiceRepoResume(self.subparser)
70067006
self.repo_worm_cmd = BackupServiceRepoWORM(self.subparser)
7007+
self.repo_cloud_config_cmd = BackupServiceRepoCloudConfig(self.subparser)
70077008
self.plan_cmd = BackupServicePlan(self.subparser)
70087009
self.nodeThreads_cmd = BackupServiceNodeThreadsMap(self.subparser)
70097010

70107011
def execute(self, opts):
70117012
subcommands = ['settings', 'repository', 'repo-list', 'repo-get', 'repo-add', 'repo-archive', 'repo-remove',
7012-
'repo-pause', 'repo-resume', 'repo-worm', 'plan', 'node-threads']
7013+
'repo-pause', 'repo-resume', 'repo-worm', 'repo-cloud-config', 'plan', 'node-threads']
70137014

70147015
if opts.sub_cmd is None or opts.sub_cmd not in subcommands:
70157016
_exit_if_errors([f'<subcommand> must be one of {subcommands}'])
@@ -7034,6 +7035,8 @@ def execute(self, opts):
70347035
self.repo_resume_cmd.execute(opts)
70357036
elif opts.sub_cmd == 'repo-worm':
70367037
self.repo_worm_cmd.execute(opts)
7038+
elif opts.sub_cmd == 'repo-cloud-config':
7039+
self.repo_cloud_config_cmd.execute(opts)
70377040
elif opts.sub_cmd == 'plan':
70387041
self.plan_cmd.execute(opts)
70397042
elif opts.sub_cmd == 'node-threads':
@@ -7579,6 +7582,48 @@ def get_description():
75797582
return 'Set WORM for a repository'
75807583

75817584

7585+
class BackupServiceRepoCloudConfig:
7586+
"""Modify the cloud config for a repository.
7587+
"""
7588+
7589+
def __init__(self, subparser):
7590+
"""setup the parser"""
7591+
self.rest = None
7592+
repository_parser = subparser.add_parser('repo-cloud-config', help='Modify the cloud config for a repository',
7593+
add_help=False, allow_abbrev=False)
7594+
7595+
repository_parser.add_argument('--id', metavar='<id>', help='The repository id', required=True)
7596+
7597+
cloud_group = repository_parser.add_argument_group('Backup repository cloud arguments')
7598+
cloud_group.add_argument('--retention-delete-versions', metavar="<0|1>", choices=["0", "1"],
7599+
help='Enable or disable deleting old object versions when pruning backups with '
7600+
'expired retention period')
7601+
7602+
@rest_initialiser(version_check=True, enterprise_check=True, cluster_init_check=True)
7603+
def execute(self, opts):
7604+
"""Run the backup-service repo-cloud-config subcommand"""
7605+
7606+
body = {}
7607+
7608+
if opts.retention_delete_versions is not None:
7609+
body["retention_delete_versions"] = opts.retention_delete_versions == "1"
7610+
7611+
if body == {}:
7612+
_exit_if_errors(['At least one cloud argument is required'])
7613+
7614+
_, errors = self.rest.modify_repository_cloud_config(opts.id, body)
7615+
_exit_if_errors(errors)
7616+
_success('Repository cloud config was modified successfully')
7617+
7618+
@staticmethod
7619+
def get_man_page_name():
7620+
return get_doc_page_name("couchbase-cli-backup-service-repo-cloud-config")
7621+
7622+
@staticmethod
7623+
def get_description():
7624+
return 'Modify the cloud config for a repository'
7625+
7626+
75827627
class BackupServiceRepository:
75837628
"""(Deprecated) This command manages backup services repositories.
75847629

cluster_manager.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2719,6 +2719,22 @@ def set_worm_for_backup_repo(self, repository_id: str, period: Optional[int], di
27192719

27202720
return self._post_json(f'{hosts[0]}/api/v1/cluster/self/repository/active/{repository_id}/worm', body)
27212721

2722+
def modify_repository_cloud_config(self, repository_id: str, body: Dict[str, Any]):
2723+
"""Modify repository cloud config
2724+
2725+
Args:
2726+
repository_id (str): The repository id
2727+
body (dict): The request body
2728+
"""
2729+
hosts, errors = self.get_hostnames_for_service(BACKUP_SERVICE)
2730+
if errors:
2731+
return None, errors
2732+
2733+
if not hosts:
2734+
raise ServiceNotAvailableException(BACKUP_SERVICE)
2735+
2736+
return self._post_json(f'{hosts[0]}/api/v1/cluster/self/repository/active/{repository_id}/cloudConfig', body)
2737+
27222738
def add_backup_active_repository(self, repository_id: str, body: Dict[str, Any], cluster: str = 'self'):
27232739
"""Add an active repository
27242740

docs/modules/cli/pages/_partials/cbcli/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
** xref:cli:cbcli/couchbase-cli-backup-service-plan.adoc[backup-service plan]
77
** xref:cli:cbcli/couchbase-cli-backup-service-repo-add.adoc[backup-service repo-add]
88
** xref:cli:cbcli/couchbase-cli-backup-service-repo-archive.adoc[backup-service repo-archive]
9+
** xref:cli:cbcli/couchbase-cli-backup-service-repo-cloud-config.adoc[backup-service repo-cloud-config]
910
** xref:cli:cbcli/couchbase-cli-backup-service-repo-get.adoc[backup-service repo-get]
1011
** xref:cli:cbcli/couchbase-cli-backup-service-repo-list.adoc[backup-service repo-list]
1112
** xref:cli:cbcli/couchbase-cli-backup-service-repo-pause.adoc[backup-service repo-pause]
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
= couchbase-cli-backup-service-repo-cloud-config(1)
2+
:description: Modify the cloud config for a backup service repository
3+
ifndef::doctype-manpage[:doctitle: backup-service repo-cloud-config]
4+
5+
ifdef::doctype-manpage[]
6+
== NAME
7+
8+
couchbase-cli-backup-service-repo-cloud-config -
9+
endif::[]
10+
Modify the cloud config for a backup service repository
11+
12+
== SYNOPSIS
13+
14+
[verse]
15+
_couchbase-cli backup-service_ [--cluster <url>] [--username <user>]
16+
[--password <password>] [--client-cert <path>]
17+
[--client-cert-password <password>] [--client-key <path>]
18+
[--client-key-password <password>] _repo-cloud-config_ [--id <id>]
19+
[--retention-delete-versions <0|1>]
20+
21+
== DESCRIPTION
22+
23+
Modifies the cloud configuration for a backup service repository. This command
24+
allows you to control cloud versioning behavior and retention settings for
25+
repositories stored in cloud object storage.
26+
27+
== OPTIONS
28+
29+
--id <id>::
30+
The repository ID. This argument is required.
31+
32+
--retention-delete-versions <0|1>::
33+
Enable or disable deleting old object versions when pruning backups with
34+
expired retention period. Set to `1` to enable deletion of old versions,
35+
or `0` to disable.
36+
37+
include::{partialsdir}/cbcli/part-common-options.adoc[]
38+
39+
include::{partialsdir}/cbcli/part-host-formats.adoc[]
40+
41+
include::{partialsdir}/cbcli/part-certificate-authentication.adoc[]
42+
43+
== EXAMPLES
44+
45+
To enable deleting old object versions when pruning backups for a repository
46+
named 'cloud-backup', run the following command:
47+
48+
----
49+
$ couchbase-cli backup-service -c 127.0.0.1:8091 -u Administrator -p password \
50+
repo-cloud-config --id cloud-backup --retention-delete-versions 1
51+
----
52+
53+
If successful, the following output will be displayed:
54+
55+
----
56+
Repository cloud config was modified successfully
57+
----
58+
59+
To disable deleting old object versions when pruning backups for the same
60+
repository:
61+
62+
----
63+
$ couchbase-cli backup-service -c 127.0.0.1:8091 -u Administrator -p password \
64+
repo-cloud-config --id cloud-backup --retention-delete-versions 0
65+
----
66+
67+
== ENVIRONMENT AND CONFIGURATION VARIABLES
68+
69+
include::{partialsdir}/cbcli/part-common-env.adoc[]
70+
71+
== SEE ALSO
72+
73+
man:couchbase-cli-backup-service[1],
74+
man:couchbase-cli-backup-service-repo-list[1],
75+
man:couchbase-cli-backup-service-repo-get[1],
76+
man:couchbase-cli-backup-service-repo-add[1],
77+
man:couchbase-cli-backup-service-repo-worm[1]
78+
79+
include::{partialsdir}/cbcli/part-footer.adoc[]
80+

docs/modules/cli/pages/cbcli/couchbase-cli-backup-service.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ man:couchbase-cli-backup-service-repo-add[1]::
4646
man:couchbase-cli-backup-service-repo-archive[1]::
4747
Archive a backup service repository.
4848

49+
man:couchbase-cli-backup-service-repo-cloud-config[1]::
50+
Modify cloud configuration for a backup service repository.
51+
4952
man:couchbase-cli-backup-service-repo-remove[1]::
5053
Remove a backup service repository.
5154

test/mock_server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@ def export_eventing_functions(rest_params=None, server_args=None, path="", endpo
633633
(r'/api/v1/cluster/self/repository/active/[\w-]+/pause$', {'POST': do_nothing}),
634634
(r'/api/v1/cluster/self/repository/active/[\w-]+/resume$', {'POST': do_nothing}),
635635
(r'/api/v1/cluster/self/repository/active/[\w-]+/worm$', {'POST': do_nothing}),
636+
(r'/api/v1/cluster/self/repository/active/[\w-]+/cloudConfig$', {'POST': do_nothing}),
636637
(r'/api/v1/plan$', {'GET': get_by_path}),
637638
(r'/api/v1/plan/\w+$', {'GET': get_by_path, 'DELETE': do_nothing, 'POST': do_nothing}),
638639
(r'/api/v1/cluster/self/repository/(:?active|archived|imported)/\w+(:?\?remove_repository=\w+)?$',

test/test_cli.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5535,6 +5535,51 @@ def test_disable_worm_success(self):
55355535
self.assertIn('Repository WORM config was set', self.str_output)
55365536

55375537

5538+
class TestBackupServiceRepoCloudConfig(CommandTest):
5539+
"""Test the backup-service repo-cloud-config subcommand"""
5540+
5541+
def setUp(self):
5542+
self.server_args = {'enterprise': True, 'init': True, 'is_admin': True,
5543+
'/pools/default/nodeServices': {'nodesExt': [{
5544+
'hostname': host,
5545+
'services': {
5546+
'backupAPI': port,
5547+
},
5548+
}]}}
5549+
self.command = ['couchbase-cli', 'backup-service'] + cluster_connect_args + ['repo-cloud-config']
5550+
super(TestBackupServiceRepoCloudConfig, self).setUp()
5551+
5552+
def test_missing_id(self):
5553+
"""Test that the command fails if --id is not provided"""
5554+
self.system_exit_run(self.command + ['--retention-delete-versions', '1'], self.server_args)
5555+
self.assertIn('--id', self.str_error)
5556+
self.assertIn('required', self.str_error)
5557+
5558+
def test_invalid_retention_delete_versions_value(self):
5559+
"""Test that --retention-delete-versions only accepts 0 or 1"""
5560+
self.system_exit_run(self.command + ['--id', 'myrepo', '--retention-delete-versions', 'yes'], self.server_args)
5561+
self.assertIn('invalid choice', self.str_error)
5562+
5563+
def test_set_retention_delete_versions_enable(self):
5564+
"""Test that the command successfully enables retention-delete-versions for a repository"""
5565+
self.no_error_run(self.command + ['--id', 'myrepo', '--retention-delete-versions', '1'], self.server_args)
5566+
self.assertIn('POST:/api/v1/cluster/self/repository/active/myrepo/cloudConfig', self.server.trace)
5567+
self.rest_parameter_match([json.dumps({'retention_delete_versions': True}, sort_keys=True)])
5568+
self.assertIn('Repository cloud config was modified successfully', self.str_output)
5569+
5570+
def test_set_retention_delete_versions_disable(self):
5571+
"""Test that the command successfully disables retention-delete-versions for a repository"""
5572+
self.no_error_run(self.command + ['--id', 'myrepo', '--retention-delete-versions', '0'], self.server_args)
5573+
self.assertIn('POST:/api/v1/cluster/self/repository/active/myrepo/cloudConfig', self.server.trace)
5574+
self.rest_parameter_match([json.dumps({'retention_delete_versions': False}, sort_keys=True)])
5575+
self.assertIn('Repository cloud config was modified successfully', self.str_output)
5576+
5577+
def test_no_cloud_options_provided(self):
5578+
"""Test that the command fails if no cloud options are provided"""
5579+
self.system_exit_run(self.command + ['--id', 'myrepo'], self.server_args)
5580+
self.assertIn('At least one cloud argument is required', self.str_output)
5581+
5582+
55385583
class TestBackupServiceSettings(CommandTest):
55395584
"""Test the backup-service settings subcommand"""
55405585

0 commit comments

Comments
 (0)