From 7c0d2c23c36a8379d09f551c013c20e9a62e519f Mon Sep 17 00:00:00 2001 From: Peter Stachowski Date: Sat, 5 Nov 2016 20:08:53 -0400 Subject: [PATCH] Add support for module-reapply command To facilitate the concept of live-update, a new command 'reapply' has been added to reapply a given module to all instances that it had previously been applied to. Originally, a module designated live-update would automatically be re-applied whenever it was updated. Adding a specific command however, allows operators/users more control over how the new payload would be distributed. Old 'modules' could be left if desired, or updated with the new command. Change-Id: Ic4cc9e9085cb40f1afbec05caeb04886137027a4 Partial-Bug: #1554903 Depends-On: I4caf4a57226dd711575cde766076fa25d16792e2 --- .../add_module_reapply-7645e34256d4c.yaml | 6 ++++ troveclient/tests/fakes.py | 3 ++ troveclient/tests/test_modules.py | 10 ++++++ troveclient/tests/test_v1_shell.py | 6 ++++ troveclient/v1/modules.py | 21 ++++++++++++ troveclient/v1/shell.py | 34 ++++++++++++++++--- 6 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/add_module_reapply-7645e34256d4c.yaml diff --git a/releasenotes/notes/add_module_reapply-7645e34256d4c.yaml b/releasenotes/notes/add_module_reapply-7645e34256d4c.yaml new file mode 100644 index 00000000..9cab9eea --- /dev/null +++ b/releasenotes/notes/add_module_reapply-7645e34256d4c.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - Add module-reapply command to facilitate + applying a module again to all instances + where it was previously applied. Bug 1554903 + diff --git a/troveclient/tests/fakes.py b/troveclient/tests/fakes.py index a3816d06..6a98e0f9 100644 --- a/troveclient/tests/fakes.py +++ b/troveclient/tests/fakes.py @@ -494,6 +494,9 @@ class FakeHTTPClient(base_client.HTTPClient): return self.get_instance_counts() return self.get_instances() + def put_modules_4321_instances(self, **kw): + return (202, {}, None) + def get_instances_modules(self, **kw): return (200, {}, None) diff --git a/troveclient/tests/test_modules.py b/troveclient/tests/test_modules.py index bffc1f6a..9decb6bf 100644 --- a/troveclient/tests/test_modules.py +++ b/troveclient/tests/test_modules.py @@ -145,3 +145,13 @@ class TestModules(testtools.TestCase): def test_instances(self): expected_query = {'include_clustered': True} self._test_instances(expected_query) + + def test_reapply(self): + resp = mock.Mock() + resp.status_code = 200 + body = None + self.modules.api.client.put = mock.Mock(return_value=(resp, body)) + self.modules.reapply(self.module_name) + self.modules.reapply(self.module) + resp.status_code = 500 + self.assertRaises(Exception, self.modules.reapply, self.module_name) diff --git a/troveclient/tests/test_v1_shell.py b/troveclient/tests/test_v1_shell.py index 04af36c6..249f23b8 100644 --- a/troveclient/tests/test_v1_shell.py +++ b/troveclient/tests/test_v1_shell.py @@ -737,6 +737,12 @@ class ShellTest(utils.TestCase): 'GET', '/modules/4321/instances?count_only=True&' 'include_clustered=True') + def test_module_reapply(self): + with mock.patch.object(troveclient.v1.modules.Module, '__repr__', + mock.Mock(return_value='4321')): + self.run_command('module-reapply 4321 --delay=5') + self.assert_called_anytime('PUT', '/modules/4321/instances') + def test_cluster_modules(self): self.run_command('cluster-modules cls-1234') self.assert_called_anytime('GET', '/clusters/cls-1234') diff --git a/troveclient/v1/modules.py b/troveclient/v1/modules.py index 95086089..36b8a1ef 100644 --- a/troveclient/v1/modules.py +++ b/troveclient/v1/modules.py @@ -167,3 +167,24 @@ class Modules(base.ManagerWithFind): query_strings['count_only'] = count_only return self._paginated(url, "instances", limit, marker, query_strings=query_strings) + + def reapply(self, module, md5=None, include_clustered=None, + batch_size=None, delay=None, force=None): + """Reapplies the specified module.""" + url = "/modules/%s/instances" % base.getid(module) + body = { + "reapply": { + } + } + if md5: + body["reapply"]["md5"] = md5 + if include_clustered is not None: + body["reapply"]["include_clustered"] = int(include_clustered) + if batch_size is not None: + body["reapply"]["batch_size"] = batch_size + if delay is not None: + body["reapply"]["batch_delay"] = delay + if force is not None: + body["reapply"]["force"] = int(force) + resp, body = self.api.client.put(url, body=body) + common.check_for_exceptions(resp, body, url) diff --git a/troveclient/v1/shell.py b/troveclient/v1/shell.py index 9131ff3d..a9761d50 100644 --- a/troveclient/v1/shell.py +++ b/troveclient/v1/shell.py @@ -1799,8 +1799,7 @@ def do_module_show(cs, args): 'Admin only.')) @utils.arg('--live_update', action='store_true', default=False, help=_('Allow module to be updated even if it is already applied ' - 'to a current instance or cluster. Automatically attempt to ' - 'reapply this module if the contents change.')) + 'to a current instance or cluster.')) @utils.arg('--priority_apply', action='store_true', default=False, help=_('Sets a priority for applying the module. All priority ' 'modules will be applied before non-priority ones. ' @@ -1880,9 +1879,7 @@ def do_module_create(cs, args): help=_('Allow all users to see this module. Admin only.')) @utils.arg('--live_update', action='store_true', default=None, help=_('Allow module to be updated or deleted even if it is ' - 'already applied to a current instance or cluster. ' - 'Automatically attempt to reapply this module if the ' - 'contents change.')) + 'already applied to a current instance or cluster.')) @utils.arg('--no_live_update', dest='live_update', action='store_false', default=None, help=_('Restricts a module from being updated or deleted if it is ' @@ -1925,6 +1922,33 @@ def do_module_update(cs, args): _print_object(updated_module) +@utils.arg('module', metavar='', type=str, + help=_('Name or ID of the module.')) +@utils.arg('--md5', metavar='', type=str, + default=None, + help=_('Reapply the module only to instances applied ' + 'with the specific md5.')) +@utils.arg('--include_clustered', action='store_true', default=False, + help=_('Include instances that are part of a cluster ' + '(default %(default)s).')) +@utils.arg('--batch_size', metavar='', type=int, + default=None, + help=_('Number of instances to reapply the module to before ' + 'sleeping.')) +@utils.arg('--delay', metavar='', type=int, + default=None, + help=_('Time to sleep in seconds between applying batches.')) +@utils.arg('--force', action='store_true', default=False, + help=_('Force reapply even on modules already having the ' + 'current MD5')) +@utils.service_type('database') +def do_module_reapply(cs, args): + """Reapply a module.""" + module = _find_module(cs, args.module) + cs.modules.reapply(module, args.md5, args.include_clustered, + args.batch_size, args.delay, args.force) + + @utils.arg('module', metavar='', help=_('ID or name of the module.')) @utils.service_type('database')