From e776382c93c2eb9abcf6e6f5f84d43e97f9ca642 Mon Sep 17 00:00:00 2001 From: Duk Loi Date: Tue, 9 Jun 2015 14:07:46 -0400 Subject: [PATCH] Add root-disable api Add api entry points for root-disable to the Trove client. Added unit tests. Change-Id: I27831eb361c2b219a9623f152b9def73a2865d67 Partially implements: blueprint root-disable DocImpact: added new root-disable CLI command --- troveclient/compat/cli.py | 5 +++ troveclient/tests/fakes.py | 3 ++ troveclient/tests/test_root.py | 56 ++++++++++++++++++++++++++++++ troveclient/tests/test_v1_shell.py | 4 +++ troveclient/v1/root.py | 15 ++++++++ troveclient/v1/shell.py | 9 +++++ 6 files changed, 92 insertions(+) create mode 100644 troveclient/tests/test_root.py diff --git a/troveclient/compat/cli.py b/troveclient/compat/cli.py index 5064c877..86b31098 100644 --- a/troveclient/compat/cli.py +++ b/troveclient/compat/cli.py @@ -268,6 +268,11 @@ class RootCommands(common.AuthedCommandsBase): except Exception: print(sys.exc_info()[1]) + def delete(self): + """Disable the instance's root user.""" + self._require('id') + print(self.dbaas.root.delete(self.id)) + def enabled(self): """Check the instance for root access.""" self._require('id') diff --git a/troveclient/tests/fakes.py b/troveclient/tests/fakes.py index 76cb2225..820a4332 100644 --- a/troveclient/tests/fakes.py +++ b/troveclient/tests/fakes.py @@ -508,6 +508,9 @@ class FakeHTTPClient(base_client.HTTPClient): def post_clusters_cls_1234_root(self, **kw): return (202, {}, {"user": {"password": "password", "name": "root"}}) + def delete_instances_1234_root(self, **kw): + return (202, {}, None) + def get_instances_1234_root(self, **kw): return (200, {}, {"rootEnabled": 'True'}) diff --git a/troveclient/tests/test_root.py b/troveclient/tests/test_root.py new file mode 100644 index 00000000..a4e9f38f --- /dev/null +++ b/troveclient/tests/test_root.py @@ -0,0 +1,56 @@ +# Copyright 2015 Tesora Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +import testtools + +from troveclient.v1 import root + +""" +Unit tests for root.py +""" + + +class RootTest(testtools.TestCase): + def setUp(self): + super(RootTest, self).setUp() + self.orig__init = root.Root.__init__ + root.Root.__init__ = mock.Mock(return_value=None) + self.root = root.Root() + self.root.api = mock.Mock() + self.root.api.client = mock.Mock() + + def tearDown(self): + super(RootTest, self).tearDown() + root.Root.__init__ = self.orig__init + + def _get_mock_method(self): + self._resp = mock.Mock() + self._body = None + self._url = None + + def side_effect_func(url, body=None): + self._body = body + self._url = url + return (self._resp, body) + + return mock.Mock(side_effect=side_effect_func) + + def test_delete(self): + self.root.api.client.delete = self._get_mock_method() + self._resp.status_code = 200 + self.root.delete(1234) + self.assertEqual('/instances/1234/root', self._url) + self._resp.status_code = 400 + self.assertRaises(Exception, self.root.delete, 1234) diff --git a/troveclient/tests/test_v1_shell.py b/troveclient/tests/test_v1_shell.py index b4a10072..161a3083 100644 --- a/troveclient/tests/test_v1_shell.py +++ b/troveclient/tests/test_v1_shell.py @@ -539,6 +539,10 @@ class ShellTest(utils.TestCase): self.run_command_clusters('root-enable cls-1234') self.assert_called_anytime('POST', '/clusters/cls-1234/root') + def test_root_disable_instance(self): + self.run_command('root-disable 1234') + self.assert_called_anytime('DELETE', '/instances/1234/root') + def test_root_show_instance(self): self.run_command('root-show 1234') self.assert_called('GET', '/instances/1234/root') diff --git a/troveclient/v1/root.py b/troveclient/v1/root.py index 99c6672b..47acd192 100644 --- a/troveclient/v1/root.py +++ b/troveclient/v1/root.py @@ -55,6 +55,21 @@ class Root(base.ManagerWithFind): common.check_for_exceptions(resp, body, uri) return body['user']['name'], body['user']['password'] + def delete(self, instance): + """Implements root-disable API. + Disables access to the root user for the specified db instance. + :param instance: The instance on which the root user is enabled + """ + self.disable_instance_root(instance) + + def disable_instance_root(self, instance): + """Implements root-disable for instances.""" + self._disable_root(self.instances_url % base.getid(instance)) + + def _disable_root(self, url): + resp, body = self.api.client.delete(url) + common.check_for_exceptions(resp, body, url) + def is_root_enabled(self, instance): """Return whether root is enabled for the instance.""" return self.is_instance_root_enabled(instance) diff --git a/troveclient/v1/shell.py b/troveclient/v1/shell.py index 7a64f176..3eb17c12 100644 --- a/troveclient/v1/shell.py +++ b/troveclient/v1/shell.py @@ -1024,6 +1024,15 @@ def do_root_enable(cs, args): utils.print_dict({'name': root[0], 'password': root[1]}) +@utils.arg('instance', metavar='', + help='ID or name of the instance.') +@utils.service_type('database') +def do_root_disable(cs, args): + """Disables root for an instance.""" + instance = _find_instance(cs, args.instance) + cs.root.disable_instance_root(instance) + + @utils.arg('instance_or_cluster', metavar='', help='ID or name of the instance or cluster.') @utils.service_type('database')