Use service catalog from authentication response
When using Mistral in multi openstack deployments, user can pass 'X-Target-Auth-Uri' in the header to let Mistral run openstack service actions in different openstack deployment. 'X-Target-Service-Catalog' can also be provided but it's optional. This patch adds 'is_target' attribute to Mistral context, if it's true, Mistral will talk to another openstack deployment, 'service_catalog' in the context can be empty or contain target service catalog provided by user, Mistral will get service catalog dynamically if it's empty; if it's false, the 'service_catalog' in context can also be empty(when auth_enable=False) or the content that get from keystone authentication response. This patch also fix the tempest failure introduced by: https://review.openstack.org/#/c/387883/ Related-Bug: #1634090 Change-Id: Iec3ed0333cd08831f0a15f77e3880f07dd89e1e8
This commit is contained in:
parent
dc65464f82
commit
bffb2476e7
@ -76,7 +76,6 @@ class MistralContext(BaseContext):
|
||||
"project_id",
|
||||
"auth_token",
|
||||
"service_catalog",
|
||||
"target_service_catalog",
|
||||
"user_name",
|
||||
"project_name",
|
||||
"roles",
|
||||
@ -85,6 +84,7 @@ class MistralContext(BaseContext):
|
||||
"redelivered",
|
||||
"expires_at",
|
||||
"trust_id",
|
||||
"is_target",
|
||||
])
|
||||
|
||||
def __repr__(self):
|
||||
@ -108,18 +108,19 @@ def set_ctx(new_ctx):
|
||||
|
||||
def context_from_headers_and_env(headers, env):
|
||||
params = _extract_auth_params_from_headers(headers)
|
||||
|
||||
auth_cacert = params['auth_cacert']
|
||||
auth_token = params['auth_token']
|
||||
auth_uri = params['auth_uri']
|
||||
project_id = params['project_id']
|
||||
user_id = params['user_id']
|
||||
user_name = params['user_name']
|
||||
target_service_catalog = params['target_service_catalog']
|
||||
is_target = params['is_target']
|
||||
|
||||
token_info = env.get('keystone.token_info')
|
||||
token_info = env.get('keystone.token_info', {})
|
||||
|
||||
if token_info and target_service_catalog is None:
|
||||
target_service_catalog = token_info['token']
|
||||
service_catalog = (params['service_catalog'] if is_target
|
||||
else token_info.get('token', {}))
|
||||
|
||||
return MistralContext(
|
||||
auth_uri=auth_uri,
|
||||
@ -127,7 +128,8 @@ def context_from_headers_and_env(headers, env):
|
||||
user_id=user_id,
|
||||
project_id=project_id,
|
||||
auth_token=auth_token,
|
||||
target_service_catalog=target_service_catalog,
|
||||
is_target=is_target,
|
||||
service_catalog=service_catalog,
|
||||
user_name=user_name,
|
||||
project_name=headers.get('X-Project-Name'),
|
||||
roles=headers.get('X-Roles', "").split(","),
|
||||
@ -137,7 +139,7 @@ def context_from_headers_and_env(headers, env):
|
||||
|
||||
|
||||
def _extract_auth_params_from_headers(headers):
|
||||
target_service_catalog = None
|
||||
service_catalog = None
|
||||
|
||||
if headers.get("X-Target-Auth-Uri"):
|
||||
params = {
|
||||
@ -148,13 +150,17 @@ def _extract_auth_params_from_headers(headers):
|
||||
'project_id': headers.get('X-Target-Project-Id'),
|
||||
'user_id': headers.get('X-Target-User-Id'),
|
||||
'user_name': headers.get('X-Target-User-Name'),
|
||||
'is_target': True
|
||||
}
|
||||
if not params['auth_token']:
|
||||
raise (exc.MistralException(
|
||||
'Target auth URI (X-Target-Auth-Uri) target auth token '
|
||||
'(X-Target-Auth-Token) must be present'))
|
||||
|
||||
target_service_catalog = _extract_service_catalog_from_headers(
|
||||
# It's possible that target service catalog is not provided, in this
|
||||
# case, Mistral needs to get target service catalog dynamically when
|
||||
# talking to target openstack deployment later on.
|
||||
service_catalog = _extract_service_catalog_from_headers(
|
||||
headers
|
||||
)
|
||||
else:
|
||||
@ -165,9 +171,10 @@ def _extract_auth_params_from_headers(headers):
|
||||
'project_id': headers.get('X-Project-Id'),
|
||||
'user_id': headers.get('X-User-Id'),
|
||||
'user_name': headers.get('X-User-Name'),
|
||||
'is_target': False
|
||||
}
|
||||
|
||||
params['target_service_catalog'] = target_service_catalog
|
||||
params['service_catalog'] = service_catalog
|
||||
|
||||
return params
|
||||
|
||||
|
@ -12,7 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from mistral import config
|
||||
from mistral import context as auth_context
|
||||
from mistral import exceptions
|
||||
from mistral.tests.unit import base
|
||||
from mistral.utils.openstack import keystone
|
||||
|
||||
@ -23,10 +24,6 @@ class KeystoneUtilsTest(base.BaseTest):
|
||||
|
||||
self.values = {'id': 'my_id'}
|
||||
|
||||
def override_config(self, name, override, group=None):
|
||||
config.CONF.set_override(name, override, group)
|
||||
self.addCleanup(config.CONF.clear_override, name, group)
|
||||
|
||||
def test_format_url_dollar_sign(self):
|
||||
url_template = "http://host:port/v1/$(id)s"
|
||||
|
||||
@ -46,3 +43,14 @@ class KeystoneUtilsTest(base.BaseTest):
|
||||
expected,
|
||||
keystone.format_url(url_template, self.values)
|
||||
)
|
||||
|
||||
def test_get_endpoint_for_project_noauth(self):
|
||||
# service_catalog is not set by default.
|
||||
auth_context.set_ctx(base.get_context())
|
||||
self.addCleanup(auth_context.set_ctx, None)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.UnauthorizedException,
|
||||
keystone.get_endpoint_for_project,
|
||||
'keystone'
|
||||
)
|
||||
|
@ -20,8 +20,10 @@ from keystoneclient.v3 import client as ks_client
|
||||
from keystoneclient.v3 import endpoints as ks_endpoints
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
|
||||
from mistral import context
|
||||
from mistral import exceptions
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -76,30 +78,29 @@ def get_endpoint_for_project(service_name=None, service_type=None):
|
||||
|
||||
service_catalog = obtain_service_catalog(ctx)
|
||||
|
||||
catalog = service_catalog.get_endpoints(
|
||||
service_endpoints = service_catalog.get_endpoints(
|
||||
service_name=service_name,
|
||||
service_type=service_type
|
||||
)
|
||||
|
||||
endpoint = None
|
||||
for service_type in catalog:
|
||||
service = catalog.get(service_type)
|
||||
for interface in service:
|
||||
for endpoints in six.itervalues(service_endpoints):
|
||||
for ep in endpoints:
|
||||
# is V3 interface?
|
||||
if 'interface' in interface:
|
||||
interface_type = interface['interface']
|
||||
if 'interface' in ep:
|
||||
interface_type = ep['interface']
|
||||
if CONF.os_actions_endpoint_type in interface_type:
|
||||
endpoint = ks_endpoints.Endpoint(
|
||||
None,
|
||||
interface,
|
||||
ep,
|
||||
loaded=True
|
||||
)
|
||||
break
|
||||
# is V2 interface?
|
||||
if 'publicURL' in interface:
|
||||
if 'publicURL' in ep:
|
||||
endpoint_data = {
|
||||
'url': interface['publicURL'],
|
||||
'region': interface['region']
|
||||
'url': ep['publicURL'],
|
||||
'region': ep['region']
|
||||
}
|
||||
endpoint = ks_endpoints.Endpoint(
|
||||
None,
|
||||
@ -122,6 +123,7 @@ def get_endpoint_for_project(service_name=None, service_type=None):
|
||||
|
||||
def obtain_service_catalog(ctx):
|
||||
token = ctx.auth_token
|
||||
|
||||
if ctx.is_trust_scoped and is_token_trust_scoped(token):
|
||||
if ctx.trust_id is None:
|
||||
raise Exception(
|
||||
@ -134,22 +136,29 @@ def obtain_service_catalog(ctx):
|
||||
include_catalog=True
|
||||
)['token']
|
||||
else:
|
||||
if not ctx.target_service_catalog:
|
||||
response = ctx.service_catalog
|
||||
|
||||
# Target service catalog may not be passed via API.
|
||||
if not response and ctx.is_target:
|
||||
response = client().tokens.get_token_data(
|
||||
token,
|
||||
include_catalog=True)['token']
|
||||
else:
|
||||
response = ctx.target_service_catalog
|
||||
include_catalog=True
|
||||
)['token']
|
||||
|
||||
if not response:
|
||||
raise exceptions.UnauthorizedException()
|
||||
|
||||
service_catalog = ks_service_catalog.ServiceCatalog.factory(response)
|
||||
|
||||
return service_catalog
|
||||
|
||||
|
||||
def get_keystone_endpoint_v2():
|
||||
return get_endpoint_for_project('keystone')
|
||||
return get_endpoint_for_project('keystone', service_type='identity')
|
||||
|
||||
|
||||
def get_keystone_url_v2():
|
||||
return get_endpoint_for_project('keystone').url
|
||||
return get_endpoint_for_project('keystone', service_type='identity').url
|
||||
|
||||
|
||||
def format_url(url_template, values):
|
||||
|
Loading…
Reference in New Issue
Block a user