Skip to content

Commit 22771cf

Browse files
committed
Merge branch 'neo' into master
* neo: MB-55715 Add support for configuring history_size_warning alert MB-55692 Add support for editing collections history field MB-55677 Fix the eshell error with -d switch Change-Id: I62cabb4a9f8cca8fcb597915487559bb4c8422b8
2 parents 5aa98d6 + c1db44c commit 22771cf

6 files changed

Lines changed: 125 additions & 19 deletions

File tree

cbmgr.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,12 +2342,14 @@ def __init__(self):
23422342
help="Alert when the disk analyzer gets stuck")
23432343
group.add_argument("--alert-memory-threshold", dest="alert_memory_threshold",
23442344
action="store_true", help="Alert when system memory usage exceeds threshold")
2345+
group.add_argument("--alert-bucket-history-size", dest="alert_bucket_history_size",
2346+
action="store_true", help="Alert when history size for a bucket reaches 90%%")
23452347

23462348
@rest_initialiser(cluster_init_check=True, version_check=True)
23472349
def execute(self, opts):
23482350
if opts.enabled == "1":
23492351
if opts.email_recipients is None:
2350-
_exit_if_errors(["--email-recipient must be set when email alerts are enabled"])
2352+
_exit_if_errors(["--email-recipients must be set when email alerts are enabled"])
23512353
if opts.email_sender is None:
23522354
_exit_if_errors(["--email-sender must be set when email alerts are enabled"])
23532355
if opts.email_host is None:
@@ -2390,6 +2392,8 @@ def execute(self, opts):
23902392
alerts.append('disk_usage_analyzer_stuck')
23912393
if opts.alert_memory_threshold:
23922394
alerts.append('memory_threshold')
2395+
if opts.alert_bucket_history_size:
2396+
alerts.append('history_size_warning')
23932397

23942398
enabled = "true"
23952399
if opts.enabled == "0":
@@ -4764,6 +4768,8 @@ def __init__(self):
47644768
help="List all of the scopes in the bucket")
47654769
group.add_argument("--create-collection", dest="create_collection", metavar="<collection>", default=None,
47664770
help="The path to the collection to make")
4771+
group.add_argument("--edit-collection", dest="edit_collection", metavar="<collection>", default=None,
4772+
help="The path to the collection to edit")
47674773
group.add_argument("--drop-collection", dest="drop_collection", metavar="<collection>", default=None,
47684774
help="The path to the collection to remove")
47694775
group.add_argument("--list-collections", dest="list_collections", metavar="<scope_list>", default=None,
@@ -4776,12 +4782,12 @@ def __init__(self):
47764782

47774783
@rest_initialiser(cluster_init_check=True, version_check=True)
47784784
def execute(self, opts):
4779-
cmds = [opts.create_scope, opts.drop_scope, opts.list_scopes, opts.create_collection, opts.drop_collection,
4780-
opts.list_collections]
4785+
cmds = [opts.create_scope, opts.drop_scope, opts.list_scopes, opts.create_collection, opts.edit_collection,
4786+
opts.drop_collection, opts.list_collections]
47814787
cmd_total = sum(cmd is not None for cmd in cmds)
47824788

4783-
args = "--create-scope, --drop-scope, --list-scopes, --create-collection, --drop-collection, or " \
4784-
"--list-collections"
4789+
args = "--create-scope, --drop-scope, --list-scopes, --create-collection, --edit-collection, " \
4790+
"--drop-collection, or --list-collections"
47854791
if cmd_total == 0:
47864792
_exit_if_errors([f'Must specify one of the following: {args}'])
47874793
elif cmd_total != 1:
@@ -4790,8 +4796,14 @@ def execute(self, opts):
47904796
if opts.max_ttl is not None and opts.create_collection is None:
47914797
_exit_if_errors(["--max-ttl can only be set with --create-collection"])
47924798

4793-
if opts.enable_history is not None and opts.create_collection is None:
4794-
_exit_if_errors(["--enable-history-retention can only be set with --create-collection"])
4799+
if opts.enable_history is not None and opts.create_collection is None and opts.edit_collection is None:
4800+
_exit_if_errors(["--enable-history-retention can only be set with --create-collection or "
4801+
"--edit-collection"])
4802+
4803+
if opts.edit_collection is not None and opts.enable_history is None:
4804+
_exit_if_errors(["--enable-history-retention should be set with --edit-collection"])
4805+
if opts.edit_collection is not None and opts.max_ttl is not None:
4806+
_exit_if_errors(["--max-ttl cannot be set with --edit-collection"])
47954807

47964808
if opts.create_scope:
47974809
self._create_scope(opts)
@@ -4801,6 +4813,8 @@ def execute(self, opts):
48014813
self._list_scopes(opts)
48024814
if opts.create_collection:
48034815
self._create_collection(opts)
4816+
if opts.edit_collection:
4817+
self._edit_collection(opts)
48044818
if opts.drop_collection:
48054819
self._drop_collection(opts)
48064820
if opts.list_collections is not None:
@@ -4828,6 +4842,12 @@ def _create_collection(self, opts):
48284842
_exit_if_errors(errors)
48294843
_success("Collection created")
48304844

4845+
def _edit_collection(self, opts):
4846+
scope, collection = self._get_scope_collection(opts.edit_collection)
4847+
_, errors = self.rest.edit_collection(opts.bucket, scope, collection, opts.enable_history)
4848+
_exit_if_errors(errors)
4849+
_success("Collection edited")
4850+
48314851
def _drop_collection(self, opts):
48324852
scope, collection = self._get_scope_collection(opts.drop_collection)
48334853
_, errors = self.rest.drop_collection(opts.bucket, scope, collection)

cluster_manager.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,8 +2347,17 @@ def create_collection(self, bucket, scope, collection, max_ttl, enable_history):
23472347

23482348
return self._post_form_encoded(url, params)
23492349

2350+
def edit_collection(self, bucket, scope, collection, enable_history):
2351+
url = f'{self.hostname}/pools/default/buckets/{urllib.parse.quote_plus(bucket)}/scopes/' \
2352+
f'{urllib.parse.quote_plus(scope)}/collections/{urllib.parse.quote_plus(collection)}'
2353+
params = {}
2354+
if enable_history:
2355+
params["history"] = one_zero_boolean_to_string(enable_history)
2356+
2357+
return self._patch_form_encoded(url, params)
2358+
23502359
def drop_collection(self, bucket, scope, collection):
2351-
url = f'{self.hostname}/pools/default/buckets/{urllib.parse.quote_plus(bucket)}/scopes/'\
2360+
url = f'{self.hostname}/pools/default/buckets/{urllib.parse.quote_plus(bucket)}/scopes/' \
23522361
f'{urllib.parse.quote_plus(scope)}/collections/{urllib.parse.quote_plus(collection)}'
23532362
return self._delete(url, None)
23542363

@@ -2504,7 +2513,10 @@ def _get(self, url, params=None):
25042513
@request
25052514
def _post_form_encoded(self, url, params):
25062515
if self.debug:
2507-
print(f'POST {url} {self._url_encode_params(params)}')
2516+
if len(params) and not isinstance(params[0], tuple):
2517+
print(f'POST {url} {params}')
2518+
else:
2519+
print(f'POST {url} {self._url_encode_params(params)}')
25082520

25092521
return self._handle_response(self.session.post(url, auth=(self.username, self.password), data=params,
25102522
verify=self.ca_cert, timeout=self.timeout, headers=self.headers))
@@ -2517,6 +2529,21 @@ def _post_json(self, url, params):
25172529
return self._handle_response(self.session.post(url, auth=(self.username, self.password), json=params,
25182530
verify=self.ca_cert, timeout=self.timeout, headers=self.headers))
25192531

2532+
@request
2533+
def _patch_form_encoded(self, url, params):
2534+
if self.debug:
2535+
print(f'PATCH {url} {self._url_encode_params(params)}')
2536+
2537+
return self._handle_response(
2538+
self.session.patch(
2539+
url, auth=(
2540+
self.username,
2541+
self.password),
2542+
data=params,
2543+
verify=self.ca_cert,
2544+
timeout=self.timeout,
2545+
headers=self.headers))
2546+
25202547
@request
25212548
def _patch_json(self, url, params):
25222549
if self.debug:

docs/modules/cli/pages/cbcli/couchbase-cli-collection-manage.adoc

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ _couchbase-cli collection-manage_ [--cluster <url>] [--username <user>]
1616
[--password <password>] [--client-cert <path>] [--client-cert-password <password>]
1717
[--client-key <path>] [--client-key-password <password>] [--bucket <bucket>]
1818
[--create-scope <scope>] [--drop-scope <scope>] [--list-scopes]
19-
[--create-collection <collection>] [--drop-collection <collection>]
20-
[--list-collections [<scope_list>]] [--max-ttl <seconds>]
21-
[--enable-history-retention <0|1>]
19+
[--create-collection <collection>] [--edit-collection <collection>]
20+
[--drop-collection <collection>] [--list-collections [<scope_list>]]
21+
[--max-ttl <seconds>] [--enable-history-retention <0|1>]
2222

2323
== DESCRIPTION
2424

@@ -58,6 +58,11 @@ include::{partialsdir}/cbcli/part-common-options.adoc[]
5858
format (scope.collection), specifying the scope in which the
5959
collection and the name to be created.
6060

61+
--edit-collection <collection>::
62+
Edits the collection in the scope. The option takes a path in dot
63+
format (scope.collection), specifying the collection in a scope which is
64+
to be edited.
65+
6166
--drop-collection <collection>::
6267
Removes the collection from the scope. The option takes a path in
6368
dot format (scope.collection), specifying the scope from which
@@ -73,12 +78,14 @@ include::{partialsdir}/cbcli/part-common-options.adoc[]
7378
collection, in seconds. If enabled and a document is mutated with
7479
no TTL or a TTL greater than than the maximum, its TTL will be set
7580
to the maximum TTL. Setting this option to 0 disables the use of
76-
max-TTL, and the largest TTL that is allowed is 2147483647.
81+
max-TTL, and the largest TTL that is allowed is 2147483647. This setting
82+
cannot be edited after a collection has been created.
7783

7884
--enable-history-retention <0|1>::
7985
Specifies whether or not the document history retention should be enabled for
8086
the collection. To enable the document history retention, set this option to
8187
"1". To disable the document history retention, set this option to "0".
88+
This setting can be edited after a collection has been created.
8289

8390

8491
include::{partialsdir}/cbcli/part-host-formats.adoc[]
@@ -98,6 +105,13 @@ furniture bucket.
98105
$ couchbase-cli collection-manage -c 192.168.1.5 -u Administrator \
99106
-p password --bucket furniture --create-collection chairs.couches
100107

108+
To edit the collection called "couches" in the chairs scope in the furniture
109+
bucket.
110+
111+
$ couchbase-cli collection-manage -c 192.168.1.5 -u Administrator \
112+
-p password --bucket furniture --edit-collection chairs.couches
113+
--enable-history-retention 1
114+
101115
To list all of the collections in the chairs and tables scopes in the furniture
102116
bucket.
103117

docs/modules/cli/pages/cbcli/couchbase-cli-setting-alert.adoc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ _couchbase-cli setting-alert_ [--cluster <url>] [--username <user>] [--password
2424
[--alert-meta-overhead] [--alert-meta-oom] [--alert-write-failed]
2525
[--alert-audit-msg-dropped] [--alert-indexer-max-ram]
2626
[--alert-timestamp-drift-exceeded] [--alert-node-time] [--alert-disk-analyzer]
27-
[--alert-memory-threshold]
27+
[--alert-memory-threshold] [--alert-bucket-history-size]
2828

2929
== DESCRIPTION
3030

@@ -140,7 +140,11 @@ include::{partialsdir}/cbcli/part-common-options.adoc[]
140140

141141
--alert-memory-threshold::
142142
Specifies that an email alert should be sent when any node's system memory
143-
usage exceeds a threshold
143+
usage exceeds a threshold.
144+
145+
--alert-bucket-history-size::
146+
Specifies that an email alert should be sent when the size of history for a
147+
bucket reaches 90% of the maximum history size.
144148

145149
include::{partialsdir}/cbcli/part-host-formats.adoc[]
146150

@@ -172,7 +176,8 @@ following command below:
172176
--alert-ip-changed --alert-disk-space --alert-meta-overhead \
173177
--alert-meta-oom --alert-write-failed --alert-audit-msg-dropped \
174178
--alert-indexer-max-ram --alert-timestamp-drift-exceeded \
175-
--alert-node-time --alert-disk-analyzer --alert-memory-threshold
179+
--alert-node-time --alert-disk-analyzer --alert-memory-threshold \
180+
--alert-bucket-history-size
176181

177182
To disable email alerts run the following command:
178183

test/mock_server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ def export_eventing_functions(rest_params=None, server_args=None, path="", endpo
518518
(r'/pools/default/buckets/\w+/scopes/\w+$', {'DELETE': do_nothing}),
519519
(r'/pools/default/buckets/\w+/scopes/\w+/collections$', {'POST': do_nothing}),
520520
(r'/pools/default/buckets/\w+/scopes/\w+/collections/\w+$', {'DELETE': do_nothing}),
521+
(r'/pools/default/buckets/\w+/scopes/\w+/collections/\w+$', {'PATCH': do_nothing}),
521522
(r'/pools/default/trustedCAs/\d+$', {'DELETE': get_by_path}),
522523
(r'/pools/nodes', {'GET': get_by_path}),
523524
(r'/settings/analytics', {'POST': do_nothing, 'GET': get_by_path}),

test/test_cli.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,8 @@ def setUp(self):
11021102
'--alert-auto-failover-disable', '--alert-ip-changed', '--alert-disk-space',
11031103
'--alert-meta-overhead', '--alert-meta-oom', '--alert-write-failed',
11041104
'--alert-audit-msg-dropped', '--alert-indexer-max-ram', '--alert-timestamp-drift-exceeded',
1105-
'--alert-communication-issue', '--alert-node-time', '--alert-disk-analyzer']
1105+
'--alert-communication-issue', '--alert-node-time', '--alert-disk-analyzer',
1106+
'--alert-bucket-history-size']
11061107
self.server_args = {'enterprise': True, 'init': True, 'is_admin': True}
11071108
super(TestSettingAlert, self).setUp()
11081109

@@ -1112,7 +1113,8 @@ def test_set_all_alerts(self):
11121113
'_nodes_down%2Cauto_failover_cluster_too_small%2Cauto_failover_disabled%2Cip%2Cdisk%2' +
11131114
'Coverhead%2Cep_oom_errors%2Cep_item_commit_failed%2Caudit_dropped_events%2Cindexer_ram_' +
11141115
'max_usage%2Cep_clock_cas_drift_threshold_exceeded%2Ccommunication_issue' +
1115-
'%2Ctime_out_of_sync%2Cdisk_usage_analyzer_stuck', 'enabled=false', 'emailEncrypt=false']
1116+
'%2Ctime_out_of_sync%2Cdisk_usage_analyzer_stuck%2Chistory_size_warning', 'enabled=false',
1117+
'emailEncrypt=false']
11161118

11171119
self.assertIn('POST:/settings/alerts', self.server.trace)
11181120
self.rest_parameter_match(expected_params)
@@ -1123,7 +1125,7 @@ def test_set_all_alerts_and_email(self):
11231125
'_nodes_down%2Cauto_failover_cluster_too_small%2Cauto_failover_disabled%2Cip%2Cdisk%2' +
11241126
'Coverhead%2Cep_oom_errors%2Cep_item_commit_failed%2Caudit_dropped_events%2Cindexer_ram_' +
11251127
'max_usage%2Cep_clock_cas_drift_threshold_exceeded%2Ccommunication_issue' +
1126-
'%2Ctime_out_of_sync%2Cdisk_usage_analyzer_stuck', 'enabled=true',
1128+
'%2Ctime_out_of_sync%2Cdisk_usage_analyzer_stuck%2Chistory_size_warning', 'enabled=true',
11271129
'emailEncrypt=true', 'sender=email2', 'recipients=email1', 'emailUser=emailuser',
11281130
'emailPass=emailpwd', 'emailHost=emailhost', 'emailPort=3000']
11291131

@@ -2755,6 +2757,36 @@ def setUp(self):
27552757
self.server_args = {'enterprise': True, 'init': True, 'is_admin': True}
27562758
super(TestCollectionManage, self).setUp()
27572759

2760+
def test_missing_cmd(self):
2761+
self.system_exit_run(self.command + ['--max-ttl', '100'], self.server_args)
2762+
self.assertIn("Must specify one of the following: --create-scope, --drop-scope, --list-scopes, "
2763+
"--create-collection, --edit-collection, --drop-collection, or --list-collections",
2764+
self.str_output)
2765+
2766+
def test_more_than_one_cmd(self):
2767+
self.system_exit_run(self.command + ['--create-scope', 'scope_1', '--list-collections'], self.server_args)
2768+
self.assertIn("Only one of the following may be specified: --create-scope, --drop-scope, --list-scopes, "
2769+
"--create-collection, --edit-collection, --drop-collection, or --list-collections",
2770+
self.str_output)
2771+
2772+
def test_max_ttl_set_with_not_create_collection(self):
2773+
self.system_exit_run(self.command + ['--list-collections', '--max-ttl', '100'], self.server_args)
2774+
self.assertIn("--max-ttl can only be set with --create-collection", self.str_output)
2775+
2776+
def test_enable_history_set_with_not_create_collection_or_edit_collection(self):
2777+
self.system_exit_run(self.command + ['--list-collections', '--enable-history-retention', '1'], self.server_args)
2778+
self.assertIn("--enable-history-retention can only be set with --create-collection or --edit-collection",
2779+
self.str_output)
2780+
2781+
def test_enable_history_should_be_set_with_edit_collection(self):
2782+
self.system_exit_run(self.command + ['--edit-collection', 'scope_1.collection_1'], self.server_args)
2783+
self.assertIn("--enable-history-retention should be set with --edit-collection", self.str_output)
2784+
2785+
def test_max_ttl_cannot_be_set_with_edit_collection(self):
2786+
self.system_exit_run(self.command + ['--edit-collection', 'scope_1.collection_1', "--max-ttl", "100"],
2787+
self.server_args)
2788+
self.assertIn("--max-ttl can only be set with --create-collection", self.str_output)
2789+
27582790
def test_create_scope(self):
27592791
self.no_error_run(self.command + ['--create-scope', 'scope_1'], self.server_args)
27602792
self.assertIn('POST:/pools/default/buckets/name/scopes', self.server.trace)
@@ -2780,6 +2812,13 @@ def test_create_collection(self):
27802812
expected_params = ['name=collection_1', 'maxTTL=100', 'history=true']
27812813
self.rest_parameter_match(expected_params)
27822814

2815+
def test_edit_collection(self):
2816+
self.no_error_run(self.command + ['--edit-collection', 'scope_1.collection_1', '--enable-history-retention',
2817+
'1'], self.server_args)
2818+
self.assertIn('PATCH:/pools/default/buckets/name/scopes/scope_1/collections/collection_1', self.server.trace)
2819+
expected_params = ['history=true']
2820+
self.rest_parameter_match(expected_params)
2821+
27832822
def test_delete_collection(self):
27842823
self.no_error_run(self.command + ['--drop-collection', 'scope_1.collection_1'], self.server_args)
27852824
self.assertIn('DELETE:/pools/default/buckets/name/scopes/scope_1/collections/collection_1', self.server.trace)

0 commit comments

Comments
 (0)