From 7aaab7fb57608616c1e883d2e2106ccab535f1e4 Mon Sep 17 00:00:00 2001 From: pkomarov Date: Tue, 23 Apr 2019 12:51:57 +0300 Subject: [PATCH] Improve KeyStone credentials parameter handling. - Put order in keystone credentials parameters - Add keystone auth parameters: * domain_name * trust_id - Add support for environment variables: * OS_USER_ID * OS_PROJECT_ID * OS_TENANT_ID * OS_DOMAIN_NAME * OS_DOMAIN_ID * OS_USER_DOMAIN_ID * OS_PROJECT_DOMAIN_ID * OS_TRUST_ID Co-Authored-By: Federico Ressi Change-Id: I66085db690c8f72408b79b3abea9dce7c60b4ea2 --- doc/source/user/config.rst | 6 +- tobiko/openstack/keystone/_credentials.py | 106 ++++++++++++------ tobiko/openstack/keystone/config.py | 14 ++- .../openstack/keystone/test_credentials.py | 23 ++-- 4 files changed, 97 insertions(+), 52 deletions(-) diff --git a/doc/source/user/config.rst b/doc/source/user/config.rst index 1e72fdde8..8a2ee3e54 100644 --- a/doc/source/user/config.rst +++ b/doc/source/user/config.rst @@ -115,15 +115,15 @@ Currently supported variables are:: export OS_TENANT_ID=... # Domain-level authorization scope (name or ID) - export OS_DOMAIN_NAME=default + export OS_DOMAIN_NAME=Default export OS_DOMAIN_ID=... # Domain name or ID containing user - export OS_USER_DOMAIN_NAME=default + export OS_USER_DOMAIN_NAME=Default export OS_USER_DOMAIN_ID=... # Domain name or ID containing project - export OS_PROJECT_DOMAIN_NAME=default + export OS_PROJECT_DOMAIN_NAME=Default export OS_PROJECT_DOMAIN_ID=... # ID of the trust to use as a trustee user diff --git a/tobiko/openstack/keystone/_credentials.py b/tobiko/openstack/keystone/_credentials.py index 1534e6ade..10d42ada2 100644 --- a/tobiko/openstack/keystone/_credentials.py +++ b/tobiko/openstack/keystone/_credentials.py @@ -28,13 +28,15 @@ def default_keystone_credentials(): class KeystoneCredentials(collections.namedtuple( - 'KeystoneCredentials', ['auth_url', + 'KeystoneCredentials', ['api_version', + 'auth_url', 'username', - 'project_name', 'password', - 'api_version', + 'project_name', + 'domain_name', 'user_domain_name', - 'project_domain_name'])): + 'project_domain_name', + 'trust_id'])): def to_dict(self): return collections.OrderedDict( @@ -50,7 +52,7 @@ class KeystoneCredentials(collections.namedtuple( ", ".join("{!s}={!r}".format(k, v) for k, v in params.items())) - required_params = ('auth_url', 'username', 'project_name', 'password') + required_params = ('auth_url', 'username', 'password', 'project_name') def validate(self, required_params=None): required_params = required_params or self.required_params @@ -63,14 +65,25 @@ class KeystoneCredentials(collections.namedtuple( raise InvalidKeystoneCredentials(credentials=self, reason=reason) -def keystone_credentials(api_version=None, auth_url=None, - username=None, password=None, project_name=None, - user_domain_name=None, project_domain_name=None, +def keystone_credentials(api_version=None, + auth_url=None, + username=None, + password=None, + project_name=None, + domain_name=None, + user_domain_name=None, + project_domain_name=None, + trust_id=None, cls=KeystoneCredentials): - return cls(api_version=api_version, username=username, - password=password, project_name=project_name, - auth_url=auth_url, user_domain_name=user_domain_name, - project_domain_name=project_domain_name) + return cls(api_version=api_version, + auth_url=auth_url, + username=username, + password=password, + project_name=project_name, + domain_name=domain_name, + user_domain_name=user_domain_name, + project_domain_name=project_domain_name, + trust_id=trust_id) class InvalidKeystoneCredentials(tobiko.TobikoException): @@ -88,24 +101,46 @@ class EnvironKeystoneCredentialsFixture(tobiko.SharedFixture): LOG.debug("OS_AUTH_URL environment variable not defined") return - api_version = (config.get_int_env('OS_IDENTITY_API_VERSION') or - api_version_from_url(auth_url)) + api_version = ( + config.get_int_env('OS_IDENTITY_API_VERSION') or + api_version_from_url(auth_url)) + username = ( + config.get_env('OS_USERNAME') or + config.get_env('OS_USER_ID')) + password = config.get_env('OS_PASSWORD') + project_name = ( + config.get_env('OS_PROJECT_NAME') or + config.get_env('OS_TENANT_NAME') or + config.get_env('OS_PROJECT_ID') or + config.get_env('OS_TENANT_ID')) if api_version == 2: credentials = keystone_credentials( - api_version=api_version, auth_url=auth_url, - username=config.get_env('OS_USERNAME'), - password=config.get_env('OS_PASSWORD'), - project_name=(config.get_env('OS_PROJECT_NAME') or - config.get_env('OS_TENANT_NAME'))) + api_version=api_version, + auth_url=auth_url, + username=username, + password=password, + project_name=project_name) else: + domain_name = ( + config.get_env('OS_DOMAIN_NAME') or + config.get_env('OS_DOMAIN_ID')) + user_domain_name = ( + config.get_env('OS_USER_DOMAIN_NAME') or + config.get_env('OS_USER_DOMAIN_ID')) + project_domain_name = ( + config.get_env('OS_PROJECT_DOMAIN_NAME') or + config.get_env('OS_PROJECT_DOMAIN_ID')) + trust_id = config.get_env('OS_TRUST_ID') credentials = keystone_credentials( - api_version=api_version, auth_url=auth_url, - username=config.get_env('OS_USERNAME'), - password=config.get_env('OS_PASSWORD'), - project_name=(config.get_env('OS_PROJECT_NAME') or - config.get_env('OS_TENANT_NAME')), - user_domain_name=config.get_env('OS_USER_DOMAIN_NAME'), - project_domain_name=config.get_env('OS_PROJECT_DOMAIN_NAME')) + api_version=api_version, + auth_url=auth_url, + username=username, + password=password, + project_name=project_name, + domain_name=domain_name, + user_domain_name=user_domain_name, + project_domain_name=project_domain_name, + trust_id=trust_id) try: credentials.validate() except InvalidKeystoneCredentials as ex: @@ -131,16 +166,22 @@ class ConfigKeystoneCredentialsFixture(tobiko.SharedFixture): api_version_from_url(auth_url)) if api_version == 2: credentials = keystone_credentials( - api_version=api_version, auth_url=auth_url, - username=conf.username, password=conf.password, + api_version=api_version, + auth_url=auth_url, + username=conf.username, + password=conf.password, project_name=conf.project_name) else: credentials = keystone_credentials( - api_version=api_version, auth_url=auth_url, - username=conf.username, password=conf.password, + api_version=api_version, + auth_url=auth_url, + username=conf.username, + password=conf.password, project_name=conf.project_name, + domain_name=conf.domain_name, user_domain_name=conf.user_domain_name, - project_domain_name=conf.project_domain_name) + project_domain_name=conf.project_domain_name, + trust_id=conf.trust_id) try: credentials.validate() except InvalidKeystoneCredentials as ex: @@ -160,7 +201,6 @@ class DefaultKeystoneCredentialsFixture(tobiko.SharedFixture): credentials = None def setup_fixture(self): - for fixture in self.fixtures: try: credentials = tobiko.setup_fixture(fixture).credentials @@ -172,7 +212,6 @@ class DefaultKeystoneCredentialsFixture(tobiko.SharedFixture): fixture, credentials) self.credentials = credentials return credentials - raise RuntimeError('Unable to found any valid credentials') @@ -183,7 +222,6 @@ def api_version_from_url(auth_url): elif auth_url.endswith('/v3'): LOG.info('Got Keystone API version 3 from auth_url: %r', auth_url) return 3 - else: LOG.warning('Unable to get Keystone API version from auth_url: %r', auth_url) diff --git a/tobiko/openstack/keystone/config.py b/tobiko/openstack/keystone/config.py index ae9280bdb..a6784cdc7 100644 --- a/tobiko/openstack/keystone/config.py +++ b/tobiko/openstack/keystone/config.py @@ -20,7 +20,10 @@ def register_tobiko_options(conf): conf.register_opts( group=cfg.OptGroup('keystone'), - opts=[cfg.StrOpt('auth_url', + opts=[cfg.IntOpt('api_version', + default=None, + help="Identity API version"), + cfg.StrOpt('auth_url', default=None, help="Identity service URL"), cfg.StrOpt('username', @@ -32,12 +35,15 @@ def register_tobiko_options(conf): cfg.StrOpt('password', default=None, help="Password"), - cfg.IntOpt('api_version', + cfg.StrOpt('domain_name', default=None, - help="Identity API version"), + help="Domain name"), cfg.StrOpt('user_domain_name', default=None, help="User domain name"), cfg.StrOpt('project_domain_name', default=None, - help="Project domain name")]) + help="Project domain name"), + cfg.StrOpt('trust_id', + default=None, + help="Trust ID for trust scoping.")]) diff --git a/tobiko/tests/openstack/keystone/test_credentials.py b/tobiko/tests/openstack/keystone/test_credentials.py index 265539121..f5a56c97f 100644 --- a/tobiko/tests/openstack/keystone/test_credentials.py +++ b/tobiko/tests/openstack/keystone/test_credentials.py @@ -48,16 +48,16 @@ V3_PARAMS = { 'username': 'demo', 'password': 'super-secret', 'auth_url': 'http://10.0.0.1:5678/v3', - 'user_domain_name': 'demo', - 'project_domain_name': 'demo'} + 'user_domain_name': 'Default', + 'project_domain_name': 'Default'} V3_ENVIRON = { 'OS_PROJECT_NAME': 'demo', 'OS_USERNAME': 'demo', 'OS_PASSWORD': 'super-secret', 'OS_AUTH_URL': 'http://10.0.0.1:5678/v3', - 'OS_USER_DOMAIN_NAME': 'demo', - 'OS_PROJECT_DOMAIN_NAME': 'demo'} + 'OS_USER_DOMAIN_NAME': 'Default', + 'OS_PROJECT_DOMAIN_NAME': 'Default'} V3_ENVIRON_WITH_VERSION = dict(V3_ENVIRON, OS_IDENTITY_API_VERSION='3') @@ -75,9 +75,9 @@ class KeystoneCredentialsTest(base.OpenstackTest): credentials.validate() self.assertEqual(V2_PARAMS, credentials.to_dict()) self.assertEqual( - "keystone_credentials(auth_url='http://10.0.0.1:5678/v2.0', " - "username='demo', project_name='demo', password='***', " - "api_version=2)", + "keystone_credentials(api_version=2, " + "auth_url='http://10.0.0.1:5678/v2.0', username='demo', " + "password='***', project_name='demo')", repr(credentials)) def test_validate_from_params_v3(self): @@ -85,10 +85,11 @@ class KeystoneCredentialsTest(base.OpenstackTest): credentials.validate() self.assertEqual(V3_PARAMS, credentials.to_dict()) self.assertEqual( - "keystone_credentials(auth_url='http://10.0.0.1:5678/v3', " - "username='demo', project_name='demo', password='***', " - "api_version=3, user_domain_name='demo', " - "project_domain_name='demo')", + "keystone_credentials(api_version=3, " + "auth_url='http://10.0.0.1:5678/v3', username='demo', " + "password='***', project_name='demo', " + "user_domain_name='Default', " + "project_domain_name='Default')", repr(credentials)) def test_validate_without_auth_url(self):