Merge "Use scope in v3 identity client"
This commit is contained in:
commit
b15c5d5649
@ -26,6 +26,11 @@ can be used to:
|
||||
- Run tests for admin APIs
|
||||
- Generate test credentials on the fly (see `Dynamic Credentials`_)
|
||||
|
||||
When keystone uses a policy that requires domain scoped tokens for admin
|
||||
actions, the flag ``admin_domain_scope`` must be set to ``True``.
|
||||
The admin user configured, if any, must have a role assigned to the domain to
|
||||
be usable.
|
||||
|
||||
Tempest allows for configuring pre-provisioned test credentials as well.
|
||||
This can be done using the accounts.yaml file (see
|
||||
`Pre-Provisioned Credentials`_). This file is used to specify an arbitrary
|
||||
@ -87,6 +92,14 @@ list of role names each of which will be assigned to each of the users created
|
||||
by dynamic credentials. This option will not have any effect when Tempest is not
|
||||
configured to use dynamic credentials.
|
||||
|
||||
When the ``admin_domain_scope`` option is set to ``True``, provisioned admin
|
||||
accounts will be assigned a role on domain configured in
|
||||
``default_credentials_domain_name``. This will make the accounts provisioned
|
||||
usable in a cloud where domain scoped tokens are required by keystone for
|
||||
admin operations. Note that the the initial pre-provision admin accounts,
|
||||
configured in tempest.conf, must have a role on the same domain as well, for
|
||||
Dynamic Credentials to work.
|
||||
|
||||
|
||||
Pre-Provisioned Credentials
|
||||
"""""""""""""""""""""""""""
|
||||
@ -124,6 +137,18 @@ should have a unique project. This is required to provide proper isolation
|
||||
to the tests using the credentials, and failure to do this will likely cause
|
||||
unexpected failures in some tests.
|
||||
|
||||
When the keystone in the target cloud requires domain scoped tokens to
|
||||
perform admin actions, all pre-provisioned admin users must have a role
|
||||
assigned on the domain where test accounts a provisioned.
|
||||
The option ``admin_domain_scope`` is used to tell tempest that domain scoped
|
||||
tokens shall be used. ``default_credentials_domain_name`` is the domain where
|
||||
test accounts are expected to be provisioned if no domain is specified.
|
||||
|
||||
Note that if credentials are pre-provisioned via ``tempest account-generator``
|
||||
the role on the domain will be assigned automatically for you, as long as
|
||||
``admin_domain_scope`` as ``default_credentials_domain_name`` are configured
|
||||
properly in tempest.conf.
|
||||
|
||||
Pre-Provisioned Credentials are also know as accounts.yaml or accounts file.
|
||||
|
||||
Compute
|
||||
|
@ -98,7 +98,8 @@ class BaseTrustsV3Test(base.BaseIdentityV3AdminTest):
|
||||
password=self.trustor_password,
|
||||
user_domain_id='default',
|
||||
tenant_name=self.trustor_project_name,
|
||||
project_domain_id='default')
|
||||
project_domain_id='default',
|
||||
domain_id='default')
|
||||
os = clients.Manager(credentials=creds)
|
||||
self.trustor_client = os.trusts_client
|
||||
|
||||
@ -266,7 +267,18 @@ class TrustsV3TestJSON(BaseTrustsV3Test):
|
||||
@test.attr(type='smoke')
|
||||
@test.idempotent_id('4773ebd5-ecbf-4255-b8d8-b63e6f72b65d')
|
||||
def test_get_trusts_all(self):
|
||||
|
||||
# Simple function that can be used for cleanup
|
||||
def set_scope(auth_provider, scope):
|
||||
auth_provider.scope = scope
|
||||
|
||||
self.create_trust()
|
||||
# Listing trusts can be done by trustor, by trustee, or without
|
||||
# any filter if scoped to a project, so we must ensure token scope is
|
||||
# project for this test.
|
||||
original_scope = self.os_adm.auth_provider.scope
|
||||
set_scope(self.os_adm.auth_provider, 'project')
|
||||
self.addCleanup(set_scope, self.os_adm.auth_provider, original_scope)
|
||||
trusts_get = self.trusts_client.list_trusts()['trusts']
|
||||
trusts = [t for t in trusts_get
|
||||
if t['id'] == self.trust_id]
|
||||
|
@ -162,6 +162,12 @@ class BaseIdentityV3AdminTest(BaseIdentityV3Test):
|
||||
cls.creds_client = cls.os_adm.credentials_client
|
||||
cls.groups_client = cls.os_adm.groups_client
|
||||
cls.projects_client = cls.os_adm.projects_client
|
||||
if CONF.identity.admin_domain_scope:
|
||||
# NOTE(andreaf) When keystone policy requires it, the identity
|
||||
# admin clients for these tests shall use 'domain' scoped tokens.
|
||||
# As the client manager is already created by the base class,
|
||||
# we set the scope for the inner auth provider.
|
||||
cls.os_adm.auth_provider.scope = 'domain'
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
|
@ -193,17 +193,21 @@ def get_configured_admin_credentials(fill_in=True, identity_version=None):
|
||||
|
||||
|
||||
# Wrapper around auth.get_credentials to use the configured identity version
|
||||
# is none is specified
|
||||
# if none is specified
|
||||
def get_credentials(fill_in=True, identity_version=None, **kwargs):
|
||||
params = dict(DEFAULT_PARAMS, **kwargs)
|
||||
identity_version = identity_version or CONF.identity.auth_version
|
||||
# In case of "v3" add the domain from config if not specified
|
||||
# To honour the "default_credentials_domain_name", if not domain
|
||||
# field is specified at all, add it the credential dict.
|
||||
if identity_version == 'v3':
|
||||
domain_fields = set(x for x in auth.KeystoneV3Credentials.ATTRIBUTES
|
||||
if 'domain' in x)
|
||||
if not domain_fields.intersection(kwargs.keys()):
|
||||
domain_name = CONF.auth.default_credentials_domain_name
|
||||
params['user_domain_name'] = domain_name
|
||||
# NOTE(andreaf) Setting domain_name implicitly sets user and
|
||||
# project domain names, if they are None
|
||||
params['domain_name'] = domain_name
|
||||
|
||||
auth_url = CONF.identity.uri_v3
|
||||
else:
|
||||
|
@ -96,8 +96,15 @@ class DynamicCredentialProvider(cred_provider.CredentialProvider):
|
||||
os.networks_client, os.routers_client, os.subnets_client,
|
||||
os.ports_client, os.security_groups_client)
|
||||
else:
|
||||
return (os.identity_v3_client, os.projects_client,
|
||||
os.users_v3_client, os.roles_v3_client, os.domains_client,
|
||||
# We use a dedicated client manager for identity client in case we
|
||||
# need a different token scope for them.
|
||||
scope = 'domain' if CONF.identity.admin_domain_scope else 'project'
|
||||
identity_os = clients.Manager(self.default_admin_creds,
|
||||
scope=scope)
|
||||
return (identity_os.identity_v3_client,
|
||||
identity_os.projects_client,
|
||||
identity_os.users_v3_client, identity_os.roles_v3_client,
|
||||
identity_os.domains_client,
|
||||
os.networks_client, os.routers_client,
|
||||
os.subnets_client, os.ports_client,
|
||||
os.security_groups_client)
|
||||
@ -136,7 +143,8 @@ class DynamicCredentialProvider(cred_provider.CredentialProvider):
|
||||
self.creds_client.assign_user_role(user, project,
|
||||
self.admin_role)
|
||||
role_assigned = True
|
||||
if self.identity_version == 'v3':
|
||||
if (self.identity_version == 'v3' and
|
||||
CONF.identity.admin_domain_scope):
|
||||
self.creds_client.assign_user_role_on_domain(
|
||||
user, CONF.identity.admin_role)
|
||||
# Add roles specified in config file
|
||||
|
@ -166,6 +166,10 @@ IdentityGroup = [
|
||||
cfg.StrOpt('default_domain_id',
|
||||
default='default',
|
||||
help="ID of the default domain"),
|
||||
cfg.BoolOpt('admin_domain_scope',
|
||||
default=False,
|
||||
help="Whether keystone identity v3 policy required "
|
||||
"a domain scoped token to use admin APIs")
|
||||
]
|
||||
|
||||
identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
|
||||
|
@ -532,6 +532,7 @@ class KeystoneV3AuthProvider(KeystoneAuthProvider):
|
||||
endpoint_type = endpoint_type.replace('URL', '')
|
||||
_base_url = None
|
||||
catalog = _auth_data.get('catalog', [])
|
||||
|
||||
# Select entries with matching service type
|
||||
service_catalog = [ep for ep in catalog if ep['type'] == service]
|
||||
if len(service_catalog) > 0:
|
||||
@ -549,10 +550,20 @@ class KeystoneV3AuthProvider(KeystoneAuthProvider):
|
||||
# NOTE(andreaf) If there's no catalog at all and the service
|
||||
# is identity, it's a valid use case. Having a non-empty
|
||||
# catalog with no identity in it is not valid instead.
|
||||
msg = ('Got an empty catalog. Scope: %s. '
|
||||
'Falling back to configured URL for %s: %s')
|
||||
LOG.debug(msg, self.scope, service, self.auth_url)
|
||||
return apply_url_filters(self.auth_url, filters)
|
||||
else:
|
||||
# No matching service
|
||||
raise exceptions.EndpointNotFound(service)
|
||||
msg = ('No matching service found in the catalog.\n'
|
||||
'Scope: %s, Credentials: %s\n'
|
||||
'Auth data: %s\n'
|
||||
'Service: %s, Region: %s, endpoint_type: %s\n'
|
||||
'Catalog: %s')
|
||||
raise exceptions.EndpointNotFound(msg % (
|
||||
self.scope, self.credentials, _auth_data, service, region,
|
||||
endpoint_type, catalog))
|
||||
# Filter by endpoint type (interface)
|
||||
filtered_catalog = [ep for ep in service_catalog if
|
||||
ep['interface'] == endpoint_type]
|
||||
|
Loading…
x
Reference in New Issue
Block a user