Skip to content

Commit ce41888

Browse files
committed
MB-70102 Add subcommands for pausing/resuming backup service repos
This is needed because worm can only be enabled for a paused repo so without this it would not be possible to configure WORM just trough the CLI. Change-Id: Ie21b2eb630ec94e1308ee674ad370a6977fc62e6 Reviewed-on: https://review.couchbase.org/c/couchbase-cli/+/238414 Reviewed-by: Safian Ali <safian.ali@couchbase.com> Tested-by: Build Bot <build@couchbase.com>
1 parent a3882c0 commit ce41888

8 files changed

Lines changed: 295 additions & 2 deletions

File tree

cbmgr.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7001,12 +7001,14 @@ def __init__(self):
70017001
self.repo_add_cmd = BackupServiceRepoAdd(self.subparser)
70027002
self.repo_archive_cmd = BackupServiceRepoArchive(self.subparser)
70037003
self.repo_remove_cmd = BackupServiceRepoRemove(self.subparser)
7004+
self.repo_pause_cmd = BackupServiceRepoPause(self.subparser)
7005+
self.repo_resume_cmd = BackupServiceRepoResume(self.subparser)
70047006
self.plan_cmd = BackupServicePlan(self.subparser)
70057007
self.nodeThreads_cmd = BackupServiceNodeThreadsMap(self.subparser)
70067008

70077009
def execute(self, opts):
70087010
subcommands = ['settings', 'repository', 'repo-list', 'repo-get', 'repo-add', 'repo-archive', 'repo-remove',
7009-
'plan', 'node-threads']
7011+
'repo-pause', 'repo-resume', 'plan', 'node-threads']
70107012

70117013
if opts.sub_cmd is None or opts.sub_cmd not in subcommands:
70127014
_exit_if_errors([f'<subcommand> must be one of {subcommands}'])
@@ -7025,6 +7027,10 @@ def execute(self, opts):
70257027
self.repo_archive_cmd.execute(opts)
70267028
elif opts.sub_cmd == 'repo-remove':
70277029
self.repo_remove_cmd.execute(opts)
7030+
elif opts.sub_cmd == 'repo-pause':
7031+
self.repo_pause_cmd.execute(opts)
7032+
elif opts.sub_cmd == 'repo-resume':
7033+
self.repo_resume_cmd.execute(opts)
70287034
elif opts.sub_cmd == 'plan':
70297035
self.plan_cmd.execute(opts)
70307036
elif opts.sub_cmd == 'node-threads':
@@ -7474,6 +7480,64 @@ def get_description():
74747480
return 'Remove a repository'
74757481

74767482

7483+
class BackupServiceRepoPause:
7484+
"""Pause a repository.
7485+
"""
7486+
7487+
def __init__(self, subparser):
7488+
"""setup the parser"""
7489+
self.rest = None
7490+
repository_parser = subparser.add_parser('repo-pause', help='Pause a repository', add_help=False,
7491+
allow_abbrev=False)
7492+
7493+
repository_parser.add_argument('--id', metavar='<id>', help='The repository id', required=True)
7494+
7495+
@rest_initialiser(version_check=True, enterprise_check=True, cluster_init_check=True)
7496+
def execute(self, opts):
7497+
"""Run the backup-service repo-pause subcommand"""
7498+
7499+
_, errors = self.rest.pause_backup_repository(opts.id)
7500+
_exit_if_errors(errors)
7501+
_success('Repository was paused')
7502+
7503+
@staticmethod
7504+
def get_man_page_name():
7505+
return get_doc_page_name("couchbase-cli-backup-service-repo-pause")
7506+
7507+
@staticmethod
7508+
def get_description():
7509+
return 'Pause a repository'
7510+
7511+
7512+
class BackupServiceRepoResume:
7513+
"""Resume a repository.
7514+
"""
7515+
7516+
def __init__(self, subparser):
7517+
"""setup the parser"""
7518+
self.rest = None
7519+
repository_parser = subparser.add_parser('repo-resume', help='Resume a repository', add_help=False,
7520+
allow_abbrev=False)
7521+
7522+
repository_parser.add_argument('--id', metavar='<id>', help='The repository id', required=True)
7523+
7524+
@rest_initialiser(version_check=True, enterprise_check=True, cluster_init_check=True)
7525+
def execute(self, opts):
7526+
"""Run the backup-service repo-resume subcommand"""
7527+
7528+
_, errors = self.rest.resume_backup_repository(opts.id)
7529+
_exit_if_errors(errors)
7530+
_success('Repository was resumed')
7531+
7532+
@staticmethod
7533+
def get_man_page_name():
7534+
return get_doc_page_name("couchbase-cli-backup-service-repo-resume")
7535+
7536+
@staticmethod
7537+
def get_description():
7538+
return 'Resume a repository'
7539+
7540+
74777541
class BackupServiceRepository:
74787542
"""(Deprecated) This command manages backup services repositories.
74797543

cluster_manager.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2651,7 +2651,7 @@ def archive_backup_repository(self, repository_id, new_id, cluster='self'):
26512651
"""Archive an active repository
26522652
26532653
Args:
2654-
repository_id (str): The repository id to be retrieved
2654+
repository_id (str): The repository id
26552655
new_id (str): The id that will be given to the archived repository
26562656
cluster (str): Only 'self' is supported.
26572657
"""
@@ -2665,6 +2665,36 @@ def archive_backup_repository(self, repository_id, new_id, cluster='self'):
26652665
return self._post_json(f'{hosts[0]}/api/v1/cluster/{cluster}/repository/active/{repository_id}/archive',
26662666
{'id': new_id})
26672667

2668+
def pause_backup_repository(self, repository_id):
2669+
"""Pause an active repository
2670+
2671+
Args:
2672+
repository_id (str): The repository id
2673+
"""
2674+
hosts, errors = self.get_hostnames_for_service(BACKUP_SERVICE)
2675+
if errors:
2676+
return None, errors
2677+
2678+
if not hosts:
2679+
raise ServiceNotAvailableException(BACKUP_SERVICE)
2680+
2681+
return self._post_json(f'{hosts[0]}/api/v1/cluster/self/repository/active/{repository_id}/pause', None)
2682+
2683+
def resume_backup_repository(self, repository_id):
2684+
"""Resume an active repository
2685+
2686+
Args:
2687+
repository_id (str): The repository id
2688+
"""
2689+
hosts, errors = self.get_hostnames_for_service(BACKUP_SERVICE)
2690+
if errors:
2691+
return None, errors
2692+
2693+
if not hosts:
2694+
raise ServiceNotAvailableException(BACKUP_SERVICE)
2695+
2696+
return self._post_json(f'{hosts[0]}/api/v1/cluster/self/repository/active/{repository_id}/resume', None)
2697+
26682698
def add_backup_active_repository(self, repository_id: str, body: Dict[str, Any], cluster: str = 'self'):
26692699
"""Archive an active repository
26702700

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
** xref:cli:cbcli/couchbase-cli-backup-service-repo-archive.adoc[backup-service repo-archive]
99
** xref:cli:cbcli/couchbase-cli-backup-service-repo-get.adoc[backup-service repo-get]
1010
** xref:cli:cbcli/couchbase-cli-backup-service-repo-list.adoc[backup-service repo-list]
11+
** xref:cli:cbcli/couchbase-cli-backup-service-repo-pause.adoc[backup-service repo-pause]
1112
** xref:cli:cbcli/couchbase-cli-backup-service-repo-remove.adoc[backup-service repo-remove]
13+
** xref:cli:cbcli/couchbase-cli-backup-service-repo-resume.adoc[backup-service repo-resume]
1214
** xref:cli:cbcli/couchbase-cli-backup-service-repository.adoc[backup-service repository]
1315
** xref:cli:cbcli/couchbase-cli-backup-service-settings.adoc[backup-service settings]
1416
** xref:cli:cbcli/couchbase-cli-bucket-compact.adoc[bucket-compact]
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
= couchbase-cli-backup-service-repo-pause(1)
2+
:description: Pause a backup service repository
3+
ifndef::doctype-manpage[:doctitle: backup-service repo-pause]
4+
5+
ifdef::doctype-manpage[]
6+
== NAME
7+
8+
couchbase-cli-backup-service-repo-pause -
9+
endif::[]
10+
Pause 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-pause_ [--id <id>]
19+
20+
== DESCRIPTION
21+
22+
Pauses an active backup repository. When a repository is paused, all scheduled
23+
backup and merge tasks for that repository are temporarily suspended. The
24+
repository remains in the active state but no new backup operations will be
25+
started until the repository is resumed.
26+
27+
This command is useful when you need to temporarily stop backup operations,
28+
for example during maintenance windows or when troubleshooting issues, without
29+
permanently archiving the repository.
30+
31+
== OPTIONS
32+
33+
--id <id>::
34+
The ID of the active repository to pause. This argument is required.
35+
36+
include::{partialsdir}/cbcli/part-common-options.adoc[]
37+
38+
include::{partialsdir}/cbcli/part-host-formats.adoc[]
39+
40+
include::{partialsdir}/cbcli/part-certificate-authentication.adoc[]
41+
42+
== EXAMPLES
43+
44+
To pause an active repository with ID 'weekly-backup', run the following
45+
command.
46+
47+
----
48+
$ couchbase-cli backup-service -c 127.0.0.1:8091 -u Administrator -p password \
49+
repo-pause --id weekly-backup
50+
----
51+
52+
To resume backup operations for the paused repository, use the `repo-resume`
53+
command.
54+
55+
----
56+
$ couchbase-cli backup-service -c 127.0.0.1:8091 -u Administrator -p password \
57+
repo-resume --id weekly-backup
58+
----
59+
60+
== ENVIRONMENT AND CONFIGURATION VARIABLES
61+
62+
include::{partialsdir}/cbcli/part-common-env.adoc[]
63+
64+
== SEE ALSO
65+
66+
man:couchbase-cli-backup-service[1],
67+
man:couchbase-cli-backup-service-repo-resume[1],
68+
man:couchbase-cli-backup-service-repo-get[1],
69+
man:couchbase-cli-backup-service-repo-list[1]
70+
71+
include::{partialsdir}/cbcli/part-footer.adoc[]
72+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
= couchbase-cli-backup-service-repo-resume(1)
2+
:description: Resume a backup service repository
3+
ifndef::doctype-manpage[:doctitle: backup-service repo-resume]
4+
5+
ifdef::doctype-manpage[]
6+
== NAME
7+
8+
couchbase-cli-backup-service-repo-resume -
9+
endif::[]
10+
Resume 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-resume_ [--id <id>]
19+
20+
== DESCRIPTION
21+
22+
Resumes a paused backup repository. When a repository is resumed, all scheduled
23+
backup and merge tasks that were previously suspended will be reactivated. The
24+
repository will continue running backup operations according to its configured
25+
schedule.
26+
27+
This command is used to restore normal backup operations for a repository that
28+
was previously paused.
29+
30+
== OPTIONS
31+
32+
--id <id>::
33+
The ID of the repository to resume. This argument is required.
34+
35+
include::{partialsdir}/cbcli/part-common-options.adoc[]
36+
37+
include::{partialsdir}/cbcli/part-host-formats.adoc[]
38+
39+
include::{partialsdir}/cbcli/part-certificate-authentication.adoc[]
40+
41+
== EXAMPLES
42+
43+
To resume a paused repository with ID 'weekly-backup', run the following
44+
command.
45+
46+
----
47+
$ couchbase-cli backup-service -c 127.0.0.1:8091 -u Administrator -p password \
48+
repo-resume --id weekly-backup
49+
----
50+
51+
== ENVIRONMENT AND CONFIGURATION VARIABLES
52+
53+
include::{partialsdir}/cbcli/part-common-env.adoc[]
54+
55+
== SEE ALSO
56+
57+
man:couchbase-cli-backup-service[1],
58+
man:couchbase-cli-backup-service-repo-pause[1],
59+
man:couchbase-cli-backup-service-repo-get[1],
60+
man:couchbase-cli-backup-service-repo-list[1]
61+
62+
include::{partialsdir}/cbcli/part-footer.adoc[]
63+

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ man:couchbase-cli-backup-service-repo-archive[1]::
4949
man:couchbase-cli-backup-service-repo-remove[1]::
5050
Remove a backup service repository.
5151

52+
man:couchbase-cli-backup-service-repo-pause[1]::
53+
Pause a backup service repository.
54+
55+
man:couchbase-cli-backup-service-repo-resume[1]::
56+
Resume a backup service repository.
57+
5258
include::{partialsdir}/cbcli/part-host-formats.adoc[]
5359

5460
include::{partialsdir}/cbcli/part-certificate-authentication.adoc[]

test/mock_server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,8 @@ def export_eventing_functions(rest_params=None, server_args=None, path="", endpo
630630
(r'/api/v1/config', {'GET': get_by_path, 'PATCH': do_nothing}),
631631
(r'/api/v1/cluster/self/repository/(:?active|archived|imported)$', {'GET': get_by_path}),
632632
(r'/api/v1/cluster/self/repository/active/\w+/archive$', {'POST': do_nothing}),
633+
(r'/api/v1/cluster/self/repository/active/[\w-]+/pause$', {'POST': do_nothing}),
634+
(r'/api/v1/cluster/self/repository/active/[\w-]+/resume$', {'POST': do_nothing}),
633635
(r'/api/v1/plan$', {'GET': get_by_path}),
634636
(r'/api/v1/plan/\w+$', {'GET': get_by_path, 'DELETE': do_nothing, 'POST': do_nothing}),
635637
(r'/api/v1/cluster/self/repository/(:?active|archived|imported)/\w+(:?\?remove_repository=\w+)?$',

test/test_cli.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5410,6 +5410,60 @@ def test_remove_repository_without_delete_data(self):
54105410
self.assertIn('remove_repository=False', self.server.queries)
54115411

54125412

5413+
class TestBackupServiceRepoPause(CommandTest):
5414+
"""Test the backup-service repo-pause subcommand"""
5415+
5416+
def setUp(self):
5417+
self.server_args = {'enterprise': True, 'init': True, 'is_admin': True,
5418+
'/pools/default/nodeServices': {'nodesExt': [{
5419+
'hostname': host,
5420+
'services': {
5421+
'backupAPI': port,
5422+
},
5423+
}]}}
5424+
self.command = ['couchbase-cli', 'backup-service'] + cluster_connect_args + ['repo-pause']
5425+
super(TestBackupServiceRepoPause, self).setUp()
5426+
5427+
def test_missing_id(self):
5428+
"""Test that the command fails if --id is not provided"""
5429+
self.system_exit_run(self.command, self.server_args)
5430+
self.assertIn('--id', self.str_error)
5431+
self.assertIn('required', self.str_error)
5432+
5433+
def test_pause_repository_success(self):
5434+
"""Test that the command successfully pauses a repository"""
5435+
self.no_error_run(self.command + ['--id', 'myrepo'], self.server_args)
5436+
self.assertIn('POST:/api/v1/cluster/self/repository/active/myrepo/pause', self.server.trace)
5437+
self.assertIn('Repository was paused', self.str_output)
5438+
5439+
5440+
class TestBackupServiceRepoResume(CommandTest):
5441+
"""Test the backup-service repo-resume subcommand"""
5442+
5443+
def setUp(self):
5444+
self.server_args = {'enterprise': True, 'init': True, 'is_admin': True,
5445+
'/pools/default/nodeServices': {'nodesExt': [{
5446+
'hostname': host,
5447+
'services': {
5448+
'backupAPI': port,
5449+
},
5450+
}]}}
5451+
self.command = ['couchbase-cli', 'backup-service'] + cluster_connect_args + ['repo-resume']
5452+
super(TestBackupServiceRepoResume, self).setUp()
5453+
5454+
def test_missing_id(self):
5455+
"""Test that the command fails if --id is not provided"""
5456+
self.system_exit_run(self.command, self.server_args)
5457+
self.assertIn('--id', self.str_error)
5458+
self.assertIn('required', self.str_error)
5459+
5460+
def test_resume_repository_success(self):
5461+
"""Test that the command successfully resumes a repository"""
5462+
self.no_error_run(self.command + ['--id', 'myrepo'], self.server_args)
5463+
self.assertIn('POST:/api/v1/cluster/self/repository/active/myrepo/resume', self.server.trace)
5464+
self.assertIn('Repository was resumed', self.str_output)
5465+
5466+
54135467
class TestBackupServiceSettings(CommandTest):
54145468
"""Test the backup-service settings subcommand"""
54155469

0 commit comments

Comments
 (0)