diff --git a/kingbirdclient/commands/README.rst b/kingbirdclient/commands/README.rst new file mode 100644 index 0000000..6ae9172 --- /dev/null +++ b/kingbirdclient/commands/README.rst @@ -0,0 +1,5 @@ +=============================== +Commands +================================ + +This module helps in mapping kingbird commands to APIs. diff --git a/kingbirdclient/commands/__init__.py b/kingbirdclient/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kingbirdclient/exceptions.py b/kingbirdclient/exceptions.py index a205ed8..d7d14bb 100644 --- a/kingbirdclient/exceptions.py +++ b/kingbirdclient/exceptions.py @@ -38,3 +38,12 @@ class IllegalArgumentException(KingbirdClientException): def __init__(self, message=None): if message: self.message = message + + +class CommandError(KingbirdClientException): + message = "CommandErrorException occurred" + code = "COMMAND_ERROR_EXCEPTION" + + def __init__(self, message=None): + if message: + self.message = message diff --git a/kingbirdclient/shell.py b/kingbirdclient/shell.py index bea98f7..0fbb797 100644 --- a/kingbirdclient/shell.py +++ b/kingbirdclient/shell.py @@ -21,6 +21,7 @@ import sys from kingbirdclient import __version__ as kingbird_version from kingbirdclient.api import client +from kingbirdclient import exceptions from kingbirdclient.openstack.common import cliutils as c from cliff import app @@ -289,19 +290,6 @@ class KingbirdShell(app.App): '(Env: KINGBIRDCLIENT_INSECURE)' ) - parser.add_argument( - '--profile', - dest='profile', - metavar='HMAC_KEY', - help='HMAC key to use for encrypting context data for performance ' - 'profiling of operation. This key should be one of the ' - 'values configured for the osprofiler middleware in mistral, ' - 'it is specified in the profiler section of the mistral ' - 'configuration (i.e. /etc/kingbird/kingbird.conf). ' - 'Without the key, profiling will not be triggered even if ' - 'osprofiler is enabled on the server side.' - ) - return parser def initialize_app(self, argv): @@ -311,19 +299,33 @@ class KingbirdShell(app.App): self._set_shell_commands(self._get_commands(ver)) - do_help = ('help' in argv) or ('-h' in argv) or not argv - - # Set default for auth_url if not supplied. The default is not - # set at the parser to support use cases where auth is not enabled. - # An example use case would be a developer's environment. - if not self.options.auth_url: - if self.options.password or self.options.token: - self.options.auth_url = 'http://localhost:35357/v3' + do_help = ['help', '-h', 'bash-completion'] # bash-completion should not require authentication. - if do_help or ('bash-completion' in argv): + skip_auth = ''.join(argv) in do_help + + if skip_auth: self.options.auth_url = None + if self.options.auth_url and not self.options.token \ + and not skip_auth: + if not self.options.tenant_name: + raise exceptions.CommandError( + ("You must provide a tenant_name " + "via --os-tenantname env[OS_TENANT_NAME]") + ) + if not self.options.username: + raise exceptions.CommandError( + ("You must provide a username " + "via --os-username env[OS_USERNAME]") + ) + + if not self.options.password: + raise exceptions.CommandError( + ("You must provide a password " + "via --os-password env[OS_PASSWORD]") + ) + self.client = client.client( kingbird_url=self.options.kingbird_url, username=self.options.username, @@ -335,10 +337,17 @@ class KingbirdShell(app.App): service_type=self.options.service_type, auth_token=self.options.token, cacert=self.options.cacert, - insecure=self.options.insecure, - profile=self.options.profile + insecure=self.options.insecure ) + if not self.options.auth_url and not skip_auth: + raise exceptions.CommandError( + ("You must provide an auth url via either" + "--os-auth-url or env[OS_AUTH_URL] or " + "specify an auth_system which defines a" + " default url with --os-auth-system or env[OS_AUTH_SYSTEM]") + ) + def _set_shell_commands(self, cmds_dict): for k, v in cmds_dict.items(): self.command_manager.add_command(k, v) diff --git a/kingbirdclient/tests/test_help_and_bash_completion.py b/kingbirdclient/tests/test_help_and_bash_completion.py new file mode 100644 index 0000000..d9876d2 --- /dev/null +++ b/kingbirdclient/tests/test_help_and_bash_completion.py @@ -0,0 +1,38 @@ +# Copyright 2016 Ericsson AB. +# +# 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 re + +from testtools import matchers + +from kingbirdclient.tests import base_shell_test as base + + +class TestCLIBashCompletionV1(base.BaseShellTests): + def test_bash_completion(self): + bash_completion, stderr = self.shell('bash-completion') + self.assertIn('bash-completion', bash_completion) + self.assertFalse(stderr) + + +class TestCLIHelp(base.BaseShellTests): + def test_help(self): + required = [ + '.*?^usage: ', + '.*?^\s+help\s+print detailed help for another command' + ] + kb_help, stderr = self.shell('help') + for r in required: + self.assertThat((kb_help + stderr), + matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) diff --git a/kingbirdclient/tests/test_shell.py b/kingbirdclient/tests/test_shell.py new file mode 100644 index 0000000..367d2db --- /dev/null +++ b/kingbirdclient/tests/test_shell.py @@ -0,0 +1,133 @@ +# Copyright 2016 EricssonAB. +# +# 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 + +from kingbirdclient.tests import base_shell_test as base + + +class TestShell(base.BaseShellTests): + + @mock.patch('kingbirdclient.api.client.determine_client_version') + def test_kingbird_version(self, mock): + self.shell( + '--os-kingbird-version=v1 quota-defaults' + ) + self.assertTrue(mock.called) + kingbird_version = mock.call_args + self.assertEqual('v1', kingbird_version[0][0]) + + @mock.patch('kingbirdclient.api.client.determine_client_version') + def test_default_kingbird_version(self, mock): + default_version = 'v1.0' + self.shell('quota-defaults') + self.assertTrue(mock.called) + kingbird_version = mock.call_args + self.assertEqual(default_version, kingbird_version[0][0]) + + @mock.patch('kingbirdclient.api.client.client') + def test_env_variables(self, mock): + self.shell( + '--os-auth-url=https://127.0.0.1:35357/v3 ' + '--os-username=admin ' + '--os-password=1234 ' + '--os-tenant-name=admin ' + 'quota-defaults' + ) + self.assertTrue(mock.called) + params = mock.call_args + self.assertEqual('https://127.0.0.1:35357/v3', params[1]['auth_url']) + self.assertEqual('admin', params[1]['username']) + self.assertEqual('admin', params[1]['project_name']) + + @mock.patch('kingbirdclient.api.client.client') + def test_env_without_auth_url(self, mock): + self.shell( + '--os-username=admin ' + '--os-password=1234 ' + '--os-tenant-name=admin ' + 'quota-defaults' + ) + self.assertTrue(mock.called) + params = mock.call_args + self.assertEqual('', params[1]['auth_url']) + self.assertEqual('admin', params[1]['username']) + self.assertEqual('admin', params[1]['project_name']) + + @mock.patch('kingbirdclient.api.client.client') + def test_kb_service_type(self, mock): + self.shell('--os-service-type=synchronization') + self.assertTrue(mock.called) + parameters = mock.call_args + self.assertEqual('synchronization', parameters[1]['service_type']) + + @mock.patch('kingbirdclient.api.client.client') + def test_kb_default_service_type(self, mock): + self.shell('quota-defaults') + self.assertTrue(mock.called) + params = mock.call_args + # Default service type is synchronization + self.assertEqual('synchronization', params[1]['service_type']) + + @mock.patch('kingbirdclient.api.client.client') + def test_kb_endpoint_type(self, mock): + self.shell('--os-kingbird-endpoint-type=adminURL quota-defaults') + self.assertTrue(mock.called) + params = mock.call_args + self.assertEqual('adminURL', params[1]['endpoint_type']) + + @mock.patch('kingbirdclient.api.client.client') + def test_kb_default_endpoint_type(self, mock): + self.shell('quota-defaults') + self.assertTrue(mock.called) + params = mock.call_args + self.assertEqual('publicURL', params[1]['endpoint_type']) + + @mock.patch('kingbirdclient.api.client.client') + def test_os_auth_token(self, mock): + self.shell( + '--os-auth-token=abcd1234 ' + 'quota-defaults' + ) + self.assertTrue(mock.called) + params = mock.call_args + self.assertEqual('abcd1234', params[1]['auth_token']) + + @mock.patch('kingbirdclient.api.client.client') + def test_command_without_kingbird_url(self, mock): + self.shell( + 'quota-defaults' + ) + self.assertTrue(mock.called) + params = mock.call_args + self.assertEqual('', params[1]['kingbird_url']) + + @mock.patch('kingbirdclient.api.client.client') + def test_command_with_kingbird_url(self, mock): + self.shell( + '--os-kingbird-url=http://localhost:8118/v1 quota-defaults' + ) + self.assertTrue(mock.called) + params = mock.call_args + self.assertEqual('http://localhost:8118/v1', + params[1]['kingbird_url']) + + @mock.patch('kingbirdclient.api.client.client') + def test_command_without_project_name(self, mock): + self.shell( + 'quota-defaults' + ) + self.assertTrue(mock.called) + params = mock.call_args + self.assertEqual('', params[1]['project_name']) diff --git a/setup.cfg b/setup.cfg index 37c4bea..83ed5a9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,6 +23,10 @@ classifier = packages = kingbirdclient +[entry_points] +console_scripts = + kingbird = kingbirdclient.shell:main + [build_sphinx] source-dir = doc/source build-dir = doc/build @@ -48,4 +52,4 @@ output_file = kingbirdclient/locale/kingbirdclient.pot [build_releasenotes] all_files = 1 build-dir = releasenotes/build -source-dir = releasenotes/source \ No newline at end of file +source-dir = releasenotes/source