From daa9bdc82335b37b33d21264a770e10ee452f22c Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Wed, 19 Oct 2016 16:01:42 -0400 Subject: [PATCH] Remove support for non-keystone auth systems The support for non-keystone auth systems and plugins was deprecated with 1f11840dd84f3570330d1fcd53d1e8eea5ff7922 back in the 3.1.0 release in mitaka. Now that we're working on shoring up the support for keystone auth sessions in the client and eventually moving to remove support for the non-session HTTPClient, we should also remove the support to load non-keystone auth plugins. The whole concept of non-keystone auth systems/plugins is a legacy artifact and is a barrier to interoperability which is a goal of nova and OpenStack in general. Change-Id: Ia649db257c416ca054977812ecb3f1a8044fa584 --- novaclient/auth_plugin.py | 150 -------- novaclient/client.py | 23 +- novaclient/exceptions.py | 9 - novaclient/shell.py | 69 +--- novaclient/tests/unit/test_auth_plugins.py | 358 ------------------ novaclient/tests/unit/test_shell.py | 27 +- novaclient/v2/client.py | 6 +- .../remove-auth-system-b2cd247b8a312b72.yaml | 9 + 8 files changed, 29 insertions(+), 622 deletions(-) delete mode 100644 novaclient/auth_plugin.py delete mode 100644 novaclient/tests/unit/test_auth_plugins.py create mode 100644 releasenotes/notes/remove-auth-system-b2cd247b8a312b72.yaml diff --git a/novaclient/auth_plugin.py b/novaclient/auth_plugin.py deleted file mode 100644 index 603bf3a29..000000000 --- a/novaclient/auth_plugin.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# Copyright 2013 Spanish National Research Council. -# All Rights Reserved. -# -# 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 logging - -import pkg_resources -import six - -from novaclient import exceptions -from novaclient import utils - - -logger = logging.getLogger(__name__) - - -_discovered_plugins = {} - - -def discover_auth_systems(): - """Discover the available auth-systems. - - This won't take into account the old style auth-systems. - """ - ep_name = 'openstack.client.auth_plugin' - for ep in pkg_resources.iter_entry_points(ep_name): - try: - # FIXME(dhellmann): It would be better to use stevedore - # here, since it abstracts this difference in behavior - # between versions of setuptools, but this seemed like a - # more expedient fix. - if hasattr(ep, 'resolve') and hasattr(ep, 'require'): - auth_plugin = ep.resolve() - else: - auth_plugin = ep.load(require=False) - except (ImportError, pkg_resources.UnknownExtra, AttributeError) as e: - logger.debug("ERROR: Cannot load auth plugin %s" % ep.name) - logger.debug(e, exc_info=1) - else: - _discovered_plugins[ep.name] = auth_plugin - - -def load_auth_system_opts(parser): - """Load options needed by the available auth-systems into a parser. - - This function will try to populate the parser with options from the - available plugins. - """ - for name, auth_plugin in six.iteritems(_discovered_plugins): - add_opts_fn = getattr(auth_plugin, "add_opts", None) - if add_opts_fn: - group = parser.add_argument_group("Auth-system '%s' options" % - name) - add_opts_fn(group) - - -def load_plugin(auth_system): - if auth_system in _discovered_plugins: - return _discovered_plugins[auth_system]() - - # NOTE(aloga): If we arrive here, the plugin will be an old-style one, - # so we have to create a fake AuthPlugin for it. - return DeprecatedAuthPlugin(auth_system) - - -class BaseAuthPlugin(object): - """Base class for authentication plugins. - - An authentication plugin needs to override at least the authenticate - method to be a valid plugin. - """ - def __init__(self): - self.opts = {} - - def get_auth_url(self): - """Return the auth url for the plugin (if any).""" - return None - - @staticmethod - def add_opts(parser): - """Populate and return the parser with the options for this plugin. - - If the plugin does not need any options, it should return the same - parser untouched. - """ - return parser - - def parse_opts(self, args): - """Parse the actual auth-system options if any. - - This method is expected to populate the attribute self.opts with a - dict containing the options and values needed to make authentication. - If the dict is empty, the client should assume that it needs the same - options as the 'keystone' auth system (i.e. os_username and - os_password). - - Returns the self.opts dict. - """ - return self.opts - - def authenticate(self, cls, auth_url): - """Authenticate using plugin defined method.""" - raise exceptions.AuthSystemNotFound(self.auth_system) - - -class DeprecatedAuthPlugin(object): - """Class to mimic the AuthPlugin class for deprecated auth systems. - - Old auth systems only define two entry points: openstack.client.auth_url - and openstack.client.authenticate. This class will load those entry points - into a class similar to a valid AuthPlugin. - """ - def __init__(self, auth_system): - self.auth_system = auth_system - - def authenticate(cls, auth_url): - raise exceptions.AuthSystemNotFound(self.auth_system) - - self.opts = {} - - self.get_auth_url = lambda: None - self.authenticate = authenticate - - self._load_endpoints() - - def _load_endpoints(self): - ep_name = 'openstack.client.auth_url' - fn = utils.load_entry_point(ep_name, name=self.auth_system) - if fn: - self.get_auth_url = fn - - ep_name = 'openstack.client.authenticate' - fn = utils.load_entry_point(ep_name, name=self.auth_system) - if fn: - self.authenticate = fn - - def parse_opts(self, args): - return self.opts diff --git a/novaclient/client.py b/novaclient/client.py index 2e5f125a6..f9fecaa4f 100644 --- a/novaclient/client.py +++ b/novaclient/client.py @@ -156,8 +156,7 @@ class HTTPClient(object): service_name=None, volume_service_name=None, timings=False, bypass_url=None, os_cache=False, no_cache=True, - http_log_debug=False, auth_system='keystone', - auth_plugin=None, auth_token=None, + http_log_debug=False, auth_token=None, cacert=None, tenant_id=None, user_id=None, connection_pool=False, api_version=None, logger=None): @@ -177,13 +176,6 @@ class HTTPClient(object): # been proven invalid self.password_func = None - if auth_system and auth_system != 'keystone' and not auth_plugin: - raise exceptions.AuthSystemNotFound(auth_system) - - if not auth_url and auth_system and auth_system != 'keystone': - auth_url = auth_plugin.get_auth_url() - if not auth_url: - raise exceptions.EndpointNotFound() self.auth_url = auth_url.rstrip('/') if auth_url else auth_url self.version = 'v1.1' self.region_name = region_name @@ -217,8 +209,6 @@ class HTTPClient(object): else: self.verify_cert = True - self.auth_system = auth_system - self.auth_plugin = auth_plugin self._session = None self._current_url = None self._logger = logger or logging.getLogger(__name__) @@ -589,10 +579,7 @@ class HTTPClient(object): auth_url = self.auth_url if self.version == "v2.0": # FIXME(chris): This should be better. while auth_url: - if not self.auth_system or self.auth_system == 'keystone': - auth_url = self._v2_auth(auth_url) - else: - auth_url = self._plugin_auth(auth_url) + auth_url = self._v2_auth(auth_url) # Are we acting on behalf of another user via an # existing token? If so, our actual endpoints may @@ -659,9 +646,6 @@ class HTTPClient(object): else: raise exceptions.from_response(resp, body, url) - def _plugin_auth(self, auth_url): - return self.auth_plugin.authenticate(self, auth_url) - def _v2_auth(self, url): """Authenticate against a v2.0 auth service.""" if self.auth_token: @@ -707,7 +691,6 @@ def _construct_http_client(username=None, password=None, project_id=None, service_name=None, volume_service_name=None, timings=False, bypass_url=None, os_cache=False, no_cache=True, http_log_debug=False, - auth_system='keystone', auth_plugin=None, auth_token=None, cacert=None, tenant_id=None, user_id=None, connection_pool=False, session=None, auth=None, user_agent='python-novaclient', @@ -738,8 +721,6 @@ def _construct_http_client(username=None, password=None, project_id=None, auth_token=auth_token, insecure=insecure, timeout=timeout, - auth_system=auth_system, - auth_plugin=auth_plugin, proxy_token=proxy_token, proxy_tenant_id=proxy_tenant_id, region_name=region_name, diff --git a/novaclient/exceptions.py b/novaclient/exceptions.py index 31dd48715..d23df8ab2 100644 --- a/novaclient/exceptions.py +++ b/novaclient/exceptions.py @@ -54,15 +54,6 @@ class NoUniqueMatch(Exception): pass -class AuthSystemNotFound(Exception): - """When the user specify a AuthSystem but not installed.""" - def __init__(self, auth_system): - self.auth_system = auth_system - - def __str__(self): - return "AuthSystemNotFound: %s" % repr(self.auth_system) - - class NoTokenLookupException(Exception): """This form of authentication does not support looking up endpoints from an existing token. diff --git a/novaclient/shell.py b/novaclient/shell.py index 3a3f28529..eb802fdb9 100644 --- a/novaclient/shell.py +++ b/novaclient/shell.py @@ -23,7 +23,6 @@ import argparse import getpass import logging import sys -import warnings from keystoneauth1 import loading from oslo_utils import encodeutils @@ -41,7 +40,6 @@ except ImportError: import novaclient from novaclient import api_versions -import novaclient.auth_plugin from novaclient import client from novaclient import exceptions as exc import novaclient.extension @@ -455,12 +453,6 @@ class OpenStackComputeShell(object): default=utils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'), help=_('Defaults to env[OS_REGION_NAME].')) - parser.add_argument( - '--os-auth-system', - metavar='', - default=utils.env('OS_AUTH_SYSTEM'), - help=argparse.SUPPRESS) - parser.add_argument( '--service-type', metavar='', @@ -509,9 +501,6 @@ class OpenStackComputeShell(object): help=_("Use this API endpoint instead of the Service Catalog. " "Defaults to env[NOVACLIENT_BYPASS_URL].")) - # The auth-system-plugins might require some extra options - novaclient.auth_plugin.load_auth_system_opts(parser) - self._append_global_identity_args(parser, argv) return parser @@ -639,9 +628,6 @@ class OpenStackComputeShell(object): skip_auth = do_help or ( 'bash-completion' in argv) - # Discover available auth plugins - novaclient.auth_plugin.discover_auth_systems() - if not args.os_compute_api_version: api_version = api_versions.get_api_version( DEFAULT_MAJOR_OS_COMPUTE_API_VERSION) @@ -658,7 +644,6 @@ class OpenStackComputeShell(object): args, 'os_project_id', getattr(args, 'os_tenant_id', None)) os_auth_url = args.os_auth_url os_region_name = args.os_region_name - os_auth_system = args.os_auth_system if "v2.0" not in os_auth_url: # NOTE(andreykurilin): assume that keystone V3 is used and try to @@ -691,15 +676,6 @@ class OpenStackComputeShell(object): auth_token = getattr(args, 'os_token', None) management_url = bypass_url if bypass_url else None - if os_auth_system and os_auth_system != "keystone": - warnings.warn(_( - 'novaclient auth plugins that are not keystone are deprecated.' - ' Auth plugins should now be done as plugins to keystoneauth' - ' and selected with --os-auth-type or OS_AUTH_TYPE')) - auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_system) - else: - auth_plugin = None - if not endpoint_type: endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE @@ -717,25 +693,20 @@ class OpenStackComputeShell(object): # Expired tokens are handled by client.py:_cs_request must_auth = not (auth_token and management_url) - # Do not use Keystone session for cases with no session support. The - # presence of auth_plugin means os_auth_system is present and is not - # keystone. + # Do not use Keystone session for cases with no session support. use_session = True - if auth_plugin or bypass_url or os_cache or volume_service_name: + if bypass_url or os_cache or volume_service_name: use_session = False # FIXME(usrleon): Here should be restrict for project id same as # for os_username or os_password but for compatibility it is not. if must_auth and not skip_auth: - if auth_plugin: - auth_plugin.parse_opts(args) - if not auth_plugin or not auth_plugin.opts: - if not os_username and not os_user_id: - raise exc.CommandError( - _("You must provide a username " - "or user ID via --os-username, --os-user-id, " - "env[OS_USERNAME] or env[OS_USER_ID]")) + if not os_username and not os_user_id: + raise exc.CommandError( + _("You must provide a username " + "or user ID via --os-username, --os-user-id, " + "env[OS_USERNAME] or env[OS_USER_ID]")) if not any([os_project_name, os_project_id]): raise exc.CommandError(_("You must provide a project name or" @@ -746,16 +717,9 @@ class OpenStackComputeShell(object): " interchangeably.")) if not os_auth_url: - if os_auth_system and os_auth_system != 'keystone': - os_auth_url = auth_plugin.get_auth_url() - - if not os_auth_url: - raise exc.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]")) + raise exc.CommandError( + _("You must provide an auth url " + "via either --os-auth-url or env[OS_AUTH_URL].")) if use_session: # Not using Nova auth plugin, so use keystone @@ -792,8 +756,7 @@ class OpenStackComputeShell(object): auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, - service_name=service_name, auth_system=os_auth_system, - auth_plugin=auth_plugin, auth_token=auth_token, + service_name=service_name, auth_token=auth_token, volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=args.debug, @@ -857,8 +820,7 @@ class OpenStackComputeShell(object): auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, - service_name=service_name, auth_system=os_auth_system, - auth_plugin=auth_plugin, auth_token=auth_token, + service_name=service_name, auth_token=auth_token, volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=args.debug, @@ -870,11 +832,6 @@ class OpenStackComputeShell(object): if must_auth: helper = SecretsHelper(args, self.cs.client) self.cs.client.keyring_saver = helper - if (auth_plugin and auth_plugin.opts and - "os_password" not in auth_plugin.opts): - use_pw = False - else: - use_pw = True tenant_id = helper.tenant_id # Allow commandline to override cache @@ -887,7 +844,7 @@ class OpenStackComputeShell(object): self.cs.client.auth_token = auth_token self.cs.client.management_url = management_url self.cs.client.password_func = lambda: helper.password - elif use_pw: + else: # We're missing something, so auth with user/pass and save # the result in our helper. self.cs.client.password = helper.password diff --git a/novaclient/tests/unit/test_auth_plugins.py b/novaclient/tests/unit/test_auth_plugins.py deleted file mode 100644 index 78dd393da..000000000 --- a/novaclient/tests/unit/test_auth_plugins.py +++ /dev/null @@ -1,358 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# 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 argparse - -from keystoneauth1 import fixture -import mock -import pkg_resources -import requests - -try: - import json -except ImportError: - import simplejson as json - -from novaclient import auth_plugin -from novaclient import client -from novaclient import exceptions -from novaclient.tests.unit import utils - - -def mock_http_request(resp=None): - """Mock an HTTP Request.""" - if not resp: - resp = fixture.V2Token() - resp.set_scope() - s = resp.add_service('compute') - s.add_endpoint("http://localhost:8774/v1.1", region='RegionOne') - - auth_response = utils.TestResponse({ - "status_code": 200, - "text": json.dumps(resp), - }) - return mock.Mock(return_value=(auth_response)) - - -def requested_headers(cs): - """Return requested passed headers.""" - return { - 'User-Agent': cs.client.USER_AGENT, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - } - - -class DeprecatedAuthPluginTest(utils.TestCase): - def test_auth_system_success(self): - class MockEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return self.authenticate - - def resolve(self): - return self.authenticate - - def authenticate(self, cls, auth_url): - cls._authenticate(auth_url, {"fake": "me"}) - - def mock_iter_entry_points(_type, name): - if _type == 'openstack.client.authenticate': - return [MockEntrypoint("fake", "fake", ["fake"])] - else: - return [] - - mock_request = mock_http_request() - - @mock.patch.object(pkg_resources, "iter_entry_points", - mock_iter_entry_points) - @mock.patch.object(requests, "request", mock_request) - def test_auth_call(): - plugin = auth_plugin.DeprecatedAuthPlugin("fake") - cs = client.Client("2", "username", "password", "project_id", - utils.AUTH_URL_V2, auth_system="fake", - auth_plugin=plugin) - cs.client.authenticate() - - headers = requested_headers(cs) - token_url = cs.client.auth_url + "/tokens" - - mock_request.assert_called_with( - "POST", - token_url, - headers=headers, - data='{"fake": "me"}', - allow_redirects=True, - **self.TEST_REQUEST_BASE) - - test_auth_call() - - def test_auth_system_not_exists(self): - def mock_iter_entry_points(_t, name=None): - return [pkg_resources.EntryPoint("fake", "fake", ["fake"])] - - mock_request = mock_http_request() - - @mock.patch.object(pkg_resources, "iter_entry_points", - mock_iter_entry_points) - @mock.patch.object(requests, "request", mock_request) - def test_auth_call(): - auth_plugin.discover_auth_systems() - plugin = auth_plugin.DeprecatedAuthPlugin("notexists") - cs = client.Client("2", "username", "password", "project_id", - utils.AUTH_URL_V2, auth_system="notexists", - auth_plugin=plugin) - self.assertRaises(exceptions.AuthSystemNotFound, - cs.client.authenticate) - - test_auth_call() - - def test_auth_system_defining_auth_url(self): - class MockAuthUrlEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return self.auth_url - - def resolve(self): - return self.auth_url - - def auth_url(self): - return "http://faked/v2.0" - - class MockAuthenticateEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return self.authenticate - - def resolve(self): - return self.authenticate - - def authenticate(self, cls, auth_url): - cls._authenticate(auth_url, {"fake": "me"}) - - def mock_iter_entry_points(_type, name): - if _type == 'openstack.client.auth_url': - return [MockAuthUrlEntrypoint("fakewithauthurl", - "fakewithauthurl", - ["auth_url"])] - elif _type == 'openstack.client.authenticate': - return [MockAuthenticateEntrypoint("fakewithauthurl", - "fakewithauthurl", - ["authenticate"])] - else: - return [] - - mock_request = mock_http_request() - - @mock.patch.object(pkg_resources, "iter_entry_points", - mock_iter_entry_points) - @mock.patch.object(requests, "request", mock_request) - def test_auth_call(): - plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl") - cs = client.Client("2", "username", "password", "project_id", - auth_system="fakewithauthurl", - auth_plugin=plugin) - cs.client.authenticate() - self.assertEqual("http://faked/v2.0", cs.client.auth_url) - - test_auth_call() - - @mock.patch.object(pkg_resources, "iter_entry_points") - def test_client_raises_exc_without_auth_url(self, mock_iter_entry_points): - class MockAuthUrlEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return self.auth_url - - def resolve(self): - return self.auth_url - - def auth_url(self): - return None - - mock_iter_entry_points.side_effect = lambda _t, name: [ - MockAuthUrlEntrypoint("fakewithauthurl", - "fakewithauthurl", - ["auth_url"])] - - plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl") - self.assertRaises( - exceptions.EndpointNotFound, - client.Client, "2", "username", "password", "project_id", - auth_system="fakewithauthurl", auth_plugin=plugin) - - -class AuthPluginTest(utils.TestCase): - @mock.patch.object(requests, "request") - @mock.patch.object(pkg_resources, "iter_entry_points") - def test_auth_system_success(self, mock_iter_entry_points, mock_request): - """Test that we can authenticate using the auth system.""" - class MockEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return FakePlugin - - def resolve(self): - return FakePlugin - - class FakePlugin(auth_plugin.BaseAuthPlugin): - def authenticate(self, cls, auth_url): - cls._authenticate(auth_url, {"fake": "me"}) - - mock_iter_entry_points.side_effect = lambda _t, name=None: [ - MockEntrypoint("fake", "fake", ["FakePlugin"])] - - mock_request.side_effect = mock_http_request() - - auth_plugin.discover_auth_systems() - plugin = auth_plugin.load_plugin("fake") - cs = client.Client("2", "username", "password", "project_id", - utils.AUTH_URL_V2, auth_system="fake", - auth_plugin=plugin) - cs.client.authenticate() - - headers = requested_headers(cs) - token_url = cs.client.auth_url + "/tokens" - - mock_request.assert_called_with( - "POST", - token_url, - headers=headers, - data='{"fake": "me"}', - allow_redirects=True, - **self.TEST_REQUEST_BASE) - - @mock.patch.object(pkg_resources, "iter_entry_points") - def test_discover_auth_system_options(self, mock_iter_entry_points): - """Test that we can load the auth system options.""" - class FakePlugin(auth_plugin.BaseAuthPlugin): - @staticmethod - def add_opts(parser): - parser.add_argument('--auth_system_opt', - default=False, - action='store_true', - help="Fake option") - return parser - - class MockEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return FakePlugin - - def resolve(self): - return FakePlugin - - mock_iter_entry_points.side_effect = lambda _t, name=None: [ - MockEntrypoint("fake", "fake", ["FakePlugin"])] - - parser = argparse.ArgumentParser() - auth_plugin.discover_auth_systems() - auth_plugin.load_auth_system_opts(parser) - opts, args = parser.parse_known_args(['--auth_system_opt']) - - self.assertTrue(opts.auth_system_opt) - - @mock.patch.object(pkg_resources, "iter_entry_points") - def test_parse_auth_system_options(self, mock_iter_entry_points): - """Test that we can parse the auth system options.""" - class MockEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return FakePlugin - - def resolve(self): - return FakePlugin - - class FakePlugin(auth_plugin.BaseAuthPlugin): - def __init__(self): - self.opts = {"fake_argument": True} - - def parse_opts(self, args): - return self.opts - - mock_iter_entry_points.side_effect = lambda _t, name=None: [ - MockEntrypoint("fake", "fake", ["FakePlugin"])] - - auth_plugin.discover_auth_systems() - plugin = auth_plugin.load_plugin("fake") - - plugin.parse_opts([]) - self.assertIn("fake_argument", plugin.opts) - - @mock.patch.object(pkg_resources, "iter_entry_points") - def test_auth_system_defining_url(self, mock_iter_entry_points): - """Test the auth_system defining an url.""" - class MockEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return FakePlugin - - def resolve(self): - return FakePlugin - - class FakePlugin(auth_plugin.BaseAuthPlugin): - def get_auth_url(self): - return "http://faked/v2.0" - - mock_iter_entry_points.side_effect = lambda _t, name=None: [ - MockEntrypoint("fake", "fake", ["FakePlugin"])] - - auth_plugin.discover_auth_systems() - plugin = auth_plugin.load_plugin("fake") - - cs = client.Client("2", "username", "password", "project_id", - auth_system="fakewithauthurl", - auth_plugin=plugin) - self.assertEqual("http://faked/v2.0", cs.client.auth_url) - - @mock.patch.object(pkg_resources, "iter_entry_points") - def test_exception_if_no_authenticate(self, mock_iter_entry_points): - """Test that no authenticate raises a proper exception.""" - class MockEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return FakePlugin - - def resolve(self): - return FakePlugin - - class FakePlugin(auth_plugin.BaseAuthPlugin): - pass - - mock_iter_entry_points.side_effect = lambda _t, name=None: [ - MockEntrypoint("fake", "fake", ["FakePlugin"])] - - auth_plugin.discover_auth_systems() - plugin = auth_plugin.load_plugin("fake") - - self.assertRaises( - exceptions.EndpointNotFound, - client.Client, "2", "username", "password", "project_id", - auth_system="fake", auth_plugin=plugin) - - @mock.patch.object(pkg_resources, "iter_entry_points") - def test_exception_if_no_url(self, mock_iter_entry_points): - """Test that no auth_url at all raises exception.""" - class MockEntrypoint(pkg_resources.EntryPoint): - def load(self, require=False): - return FakePlugin - - def resolve(self): - return FakePlugin - - class FakePlugin(auth_plugin.BaseAuthPlugin): - pass - - mock_iter_entry_points.side_effect = lambda _t, name=None: [ - MockEntrypoint("fake", "fake", ["FakePlugin"])] - - auth_plugin.discover_auth_systems() - plugin = auth_plugin.load_plugin("fake") - - self.assertRaises( - exceptions.EndpointNotFound, - client.Client, "2", "username", "password", "project_id", - auth_system="fake", auth_plugin=plugin) diff --git a/novaclient/tests/unit/test_shell.py b/novaclient/tests/unit/test_shell.py index bfb889c2a..0a74a93dc 100644 --- a/novaclient/tests/unit/test_shell.py +++ b/novaclient/tests/unit/test_shell.py @@ -62,15 +62,7 @@ FAKE_ENV4 = {'OS_USER_ID': 'user_id', FAKE_ENV5 = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': 'http://no.where/v2.0', - 'OS_COMPUTE_API_VERSION': '2', - 'OS_AUTH_SYSTEM': 'rackspace'} - -FAKE_ENV6 = {'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': 'http://no.where/v2.0', - 'OS_AUTH_SYSTEM': 'rackspace'} + 'OS_AUTH_URL': 'http://no.where/v2.0'} def _create_ver_list(versions): @@ -519,9 +511,7 @@ class ShellTest(utils.TestCase): def test_no_auth_url(self): required = ('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]',) + ' via either --os-auth-url or env[OS_AUTH_URL].',) self.make_env(exclude='OS_AUTH_URL') try: self.shell('list') @@ -687,7 +677,7 @@ class ShellTest(utils.TestCase): @mock.patch('novaclient.client.Client') def test_microversion_with_default_behaviour(self, mock_client): - self.make_env(fake_env=FAKE_ENV6) + self.make_env(fake_env=FAKE_ENV5) self.mock_server_version_range.return_value = ( api_versions.APIVersion("2.1"), api_versions.APIVersion("2.3")) self.shell('list') @@ -697,7 +687,7 @@ class ShellTest(utils.TestCase): @mock.patch('novaclient.client.Client') def test_microversion_with_default_behaviour_with_legacy_server( self, mock_client): - self.make_env(fake_env=FAKE_ENV6) + self.make_env(fake_env=FAKE_ENV5) self.mock_server_version_range.return_value = ( api_versions.APIVersion(), api_versions.APIVersion()) self.shell('list') @@ -777,15 +767,6 @@ class ShellTest(utils.TestCase): self.shell, '--os-compute-api-version 2.3 list') - @mock.patch('novaclient.client.Client') - def test_custom_auth_plugin(self, mock_client): - self.make_env(fake_env=FAKE_ENV5) - self.shell('list') - password = mock_client.call_args_list[0][0][2] - client_kwargs = mock_client.call_args_list[0][1] - self.assertEqual(password, 'password') - self.assertIs(client_kwargs['session'], None) - @mock.patch.object(novaclient.shell.OpenStackComputeShell, 'main') def test_main_error_handling(self, mock_compute_shell): class MyException(Exception): diff --git a/novaclient/v2/client.py b/novaclient/v2/client.py index d37ebb627..93c3f4069 100644 --- a/novaclient/v2/client.py +++ b/novaclient/v2/client.py @@ -68,7 +68,7 @@ class Client(object): service_type='compute', service_name=None, volume_service_name=None, timings=False, bypass_url=None, os_cache=False, no_cache=True, http_log_debug=False, - auth_system='keystone', auth_plugin=None, auth_token=None, + auth_token=None, cacert=None, tenant_id=None, user_id=None, connection_pool=False, session=None, auth=None, api_version=None, direct_use=True, logger=None, **kwargs): @@ -93,8 +93,6 @@ class Client(object): :param bool os_cache: OS cache :param bool no_cache: No cache :param bool http_log_debug: Enable debugging for HTTP connections - :param str auth_system: Auth system - :param str auth_plugin: Auth plugin :param str auth_token: Auth token :param str cacert: cacert :param str tenant_id: Tenant ID @@ -194,8 +192,6 @@ class Client(object): auth_token=auth_token, insecure=insecure, timeout=timeout, - auth_system=auth_system, - auth_plugin=auth_plugin, proxy_token=proxy_token, proxy_tenant_id=proxy_tenant_id, region_name=region_name, diff --git a/releasenotes/notes/remove-auth-system-b2cd247b8a312b72.yaml b/releasenotes/notes/remove-auth-system-b2cd247b8a312b72.yaml new file mode 100644 index 000000000..ce3fb4d0c --- /dev/null +++ b/releasenotes/notes/remove-auth-system-b2cd247b8a312b72.yaml @@ -0,0 +1,9 @@ +--- +prelude: > + The ability to use non-Keystone authentication systems has been removed. +upgrade: + - The ``--os-auth-system`` CLI option and ``OS_AUTH_SYSTEM`` environment + variable usage was deprecated in the 3.1.0 release during the Mitaka + development series. This release drops the support for using those options + to load non-Keystone authentication systems via the + ``openstack.client.auth_plugin`` extension point.