Merge "Remove v2.0 service and endpoint APIs"
This commit is contained in:
commit
2764b49bc2
@ -1,78 +0,0 @@
|
||||
.. -*- rst -*-
|
||||
|
||||
=========
|
||||
Endpoints
|
||||
=========
|
||||
|
||||
|
||||
List endpoint templates
|
||||
=======================
|
||||
|
||||
.. rest_method:: GET /v2.0/endpoints
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Response Parameters
|
||||
-------------------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- endpoints: endpoints
|
||||
|
||||
Response Example
|
||||
----------------
|
||||
|
||||
.. literalinclude:: samples/admin/endpoint-list-response.json
|
||||
:language: javascript
|
||||
|
||||
|
||||
Create endpoint template
|
||||
========================
|
||||
|
||||
.. rest_method:: POST /v2.0/endpoints
|
||||
|
||||
Normal response codes: 201
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- endpoint: endpoint
|
||||
- region: endpoint_region_request
|
||||
- service_id: service_id_request
|
||||
- publicurl: endpoint_publicurl_request
|
||||
- adminurl: endpoint_adminurl_request
|
||||
- internalurl: endpoint_internalurl_request
|
||||
|
||||
Request Example
|
||||
---------------
|
||||
|
||||
.. literalinclude:: samples/admin/endpoint-create-request.json
|
||||
:language: javascript
|
||||
|
||||
Response Parameters
|
||||
-------------------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- endpoint: endpoint
|
||||
- id: endpoint_id
|
||||
- internalurl: endpoint_internalurl_response
|
||||
- publicurl: endpoint_publicurl_response
|
||||
- region: endpoint_region_response
|
||||
|
||||
|
||||
Delete endpoint template
|
||||
========================
|
||||
|
||||
.. rest_method:: DELETE /v2.0/endpoints/{endpointId}
|
||||
|
||||
Normal response codes: 204
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- endpointId: endpoint_id_path
|
@ -9,6 +9,5 @@
|
||||
.. include:: admin-tenants.inc
|
||||
.. include:: admin-tokens.inc
|
||||
.. include:: admin-users.inc
|
||||
.. include:: admin-endpoints.inc
|
||||
.. include:: admin-versions.inc
|
||||
.. include:: admin-certificates.inc
|
||||
|
@ -142,141 +142,6 @@ Response Example
|
||||
:language: javascript
|
||||
|
||||
|
||||
Create service (admin extension)
|
||||
================================
|
||||
|
||||
.. rest_method:: POST /v2.0/OS-KSADM/services
|
||||
|
||||
Creates a service.
|
||||
|
||||
Normal response codes: 201
|
||||
Error response codes: 413,415,405,404,403,401,400,503,409
|
||||
|
||||
Request Example
|
||||
---------------
|
||||
|
||||
.. literalinclude:: samples/OS-KSADM/service-create-request.json
|
||||
:language: javascript
|
||||
|
||||
Response Parameters
|
||||
-------------------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- Location: Location
|
||||
- type: service_type
|
||||
- description: service_description
|
||||
- name: service_name
|
||||
- id: service_id
|
||||
|
||||
|
||||
List services (admin extension)
|
||||
===============================
|
||||
|
||||
.. rest_method:: GET /v2.0/OS-KSADM/services
|
||||
|
||||
Lists all services.
|
||||
|
||||
Normal response codes: 200,203
|
||||
Error response codes: 413,405,404,403,401,400,503
|
||||
|
||||
Response Parameters
|
||||
-------------------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- type: service_type
|
||||
- description: service_description
|
||||
- name: service_name
|
||||
- id: service_id
|
||||
|
||||
Response Example
|
||||
----------------
|
||||
|
||||
.. literalinclude:: samples/OS-KSADM/services-list-response.json
|
||||
:language: javascript
|
||||
|
||||
|
||||
Shows service information by ID
|
||||
===============================
|
||||
|
||||
.. rest_method:: GET /v2.0/OS-KSADM/services/{service_id}
|
||||
|
||||
Shows information for a service, by ID.
|
||||
|
||||
Normal response codes: 200,203
|
||||
Error response codes: 413,405,404,403,401,400,503
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- service_id: service_id_path
|
||||
|
||||
Response Parameters
|
||||
-------------------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- type: service_type
|
||||
- description: service_description
|
||||
- name: service_name
|
||||
- id: service_id
|
||||
|
||||
Response Example
|
||||
----------------
|
||||
|
||||
.. literalinclude:: samples/OS-KSADM/service-show-response.json
|
||||
:language: javascript
|
||||
|
||||
|
||||
Show service information by name
|
||||
================================
|
||||
|
||||
.. rest_method:: GET /v2.0/OS-KSADM/services/{serviceName}
|
||||
|
||||
Shows information for a service, by name.
|
||||
|
||||
Normal response codes: 200,203
|
||||
Error response codes: 413,405,404,403,401,400,503
|
||||
|
||||
Response Parameters
|
||||
-------------------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- serviceName: service_name_path
|
||||
- type: service_type
|
||||
- description: service_description
|
||||
- name: service_name
|
||||
- id: service_id
|
||||
|
||||
Response Example
|
||||
----------------
|
||||
|
||||
.. literalinclude:: samples/OS-KSADM/service-show-response.json
|
||||
:language: javascript
|
||||
|
||||
|
||||
Delete service (admin extension)
|
||||
================================
|
||||
|
||||
.. rest_method:: DELETE /v2.0/OS-KSADM/services/{service_id}
|
||||
|
||||
Deletes a service.
|
||||
|
||||
Normal response codes: 204
|
||||
Error response codes: 413,415,405,404,403,401,400,503,409
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- service_id: service_id_path
|
||||
|
||||
|
||||
Create a role
|
||||
=============
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"OS-KSADM:service": {
|
||||
"id": "123",
|
||||
"name": "nova",
|
||||
"type": "compute",
|
||||
"description": "OpenStack Compute Service"
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"OS-KSADM:service": {
|
||||
"id": "123",
|
||||
"name": "nova",
|
||||
"type": "compute",
|
||||
"description": "OpenStack Compute Service"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"OS-KSADM:services": [
|
||||
{
|
||||
"id": "123",
|
||||
"name": "nova",
|
||||
"type": "compute",
|
||||
"description": "OpenStack Compute Service"
|
||||
},
|
||||
{
|
||||
"id": "234",
|
||||
"name": "glance",
|
||||
"type": "image",
|
||||
"description": "OpenStack Image Service"
|
||||
}
|
||||
],
|
||||
"OS-KSADM:services_links": []
|
||||
}
|
@ -13,8 +13,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from six.moves import http_client
|
||||
|
||||
from keystone.catalog import schema
|
||||
@ -32,177 +30,6 @@ from keystone import resource
|
||||
INTERFACES = ['public', 'internal', 'admin']
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class Service(controller.V2Controller):
|
||||
|
||||
@controller.v2_deprecated
|
||||
def get_services(self, request):
|
||||
self.assert_admin(request)
|
||||
service_list = self.catalog_api.list_services()
|
||||
return {'OS-KSADM:services': service_list}
|
||||
|
||||
@controller.v2_deprecated
|
||||
def get_service(self, request, service_id):
|
||||
self.assert_admin(request)
|
||||
service_ref = self.catalog_api.get_service(service_id)
|
||||
return {'OS-KSADM:service': service_ref}
|
||||
|
||||
@controller.v2_deprecated
|
||||
def delete_service(self, request, service_id):
|
||||
self.assert_admin(request)
|
||||
self.catalog_api.delete_service(
|
||||
service_id, initiator=request.audit_initiator
|
||||
)
|
||||
|
||||
@controller.v2_deprecated
|
||||
def create_service(self, request, OS_KSADM_service):
|
||||
validation.lazy_validate(schema.service_create_v2, OS_KSADM_service)
|
||||
self.assert_admin(request)
|
||||
service_id = uuid.uuid4().hex
|
||||
service_ref = OS_KSADM_service.copy()
|
||||
service_ref['id'] = service_id
|
||||
new_service_ref = self.catalog_api.create_service(
|
||||
service_id, service_ref, initiator=request.audit_initiator)
|
||||
return {'OS-KSADM:service': new_service_ref}
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class Endpoint(controller.V2Controller):
|
||||
|
||||
@controller.v2_deprecated
|
||||
def get_endpoints(self, request):
|
||||
"""Merge matching v3 endpoint refs into legacy refs."""
|
||||
self.assert_admin(request)
|
||||
legacy_endpoints = {}
|
||||
v3_endpoints = {}
|
||||
for endpoint in self.catalog_api.list_endpoints():
|
||||
if not endpoint.get('legacy_endpoint_id'): # pure v3 endpoint
|
||||
# tell endpoints apart by the combination of
|
||||
# service_id and region_id.
|
||||
# NOTE(muyu): in theory, it's possible that there are more than
|
||||
# one endpoint of one service, one region and one interface,
|
||||
# but in practice, it makes no sense because only one will be
|
||||
# used.
|
||||
key = (endpoint['service_id'], endpoint['region_id'])
|
||||
v3_endpoints.setdefault(key, []).append(endpoint)
|
||||
else: # legacy endpoint
|
||||
if endpoint['legacy_endpoint_id'] not in legacy_endpoints:
|
||||
legacy_ep = endpoint.copy()
|
||||
legacy_ep['id'] = legacy_ep.pop('legacy_endpoint_id')
|
||||
legacy_ep.pop('interface')
|
||||
legacy_ep.pop('url')
|
||||
legacy_ep['region'] = legacy_ep.pop('region_id')
|
||||
|
||||
legacy_endpoints[endpoint['legacy_endpoint_id']] = (
|
||||
legacy_ep)
|
||||
else:
|
||||
legacy_ep = (
|
||||
legacy_endpoints[endpoint['legacy_endpoint_id']])
|
||||
|
||||
# add the legacy endpoint with an interface url
|
||||
legacy_ep['%surl' % endpoint['interface']] = endpoint['url']
|
||||
|
||||
# convert collected v3 endpoints into v2 endpoints
|
||||
for endpoints in v3_endpoints.values():
|
||||
legacy_ep = {}
|
||||
# For v3 endpoints in the same group, contents of extra attributes
|
||||
# can be different, which may cause confusion if a random one is
|
||||
# used. So only necessary attributes are used here.
|
||||
# It's different for legacy v2 endpoints, which are created
|
||||
# with the same "extra" value when being migrated.
|
||||
for key in ('service_id', 'enabled'):
|
||||
legacy_ep[key] = endpoints[0][key]
|
||||
legacy_ep['region'] = endpoints[0]['region_id']
|
||||
for endpoint in endpoints:
|
||||
# Public URL is required for v2 endpoints, so the generated v2
|
||||
# endpoint uses public endpoint's id as its id, which can also
|
||||
# be an indicator whether a public v3 endpoint is present.
|
||||
# It's safe to do so is also because that there is no v2 API to
|
||||
# get an endpoint by endpoint ID.
|
||||
if endpoint['interface'] == 'public':
|
||||
legacy_ep['id'] = endpoint['id']
|
||||
legacy_ep['%surl' % endpoint['interface']] = endpoint['url']
|
||||
|
||||
# this means there is no public URL of this group of v3 endpoints
|
||||
if 'id' not in legacy_ep:
|
||||
continue
|
||||
legacy_endpoints[legacy_ep['id']] = legacy_ep
|
||||
return {'endpoints': list(legacy_endpoints.values())}
|
||||
|
||||
@controller.v2_deprecated
|
||||
def create_endpoint(self, request, endpoint):
|
||||
"""Create three v3 endpoint refs based on a legacy ref."""
|
||||
self.assert_admin(request)
|
||||
|
||||
# according to the v2 spec publicurl is mandatory
|
||||
self._require_attribute(endpoint, 'publicurl')
|
||||
# service_id is necessary
|
||||
self._require_attribute(endpoint, 'service_id')
|
||||
|
||||
# we should check publicurl, adminurl, internalurl
|
||||
# if invalid, we should raise an exception to reject
|
||||
# the request
|
||||
for interface in INTERFACES:
|
||||
interface_url = endpoint.get(interface + 'url')
|
||||
if interface_url:
|
||||
utils.check_endpoint_url(interface_url)
|
||||
|
||||
if endpoint.get('region') is not None:
|
||||
try:
|
||||
self.catalog_api.get_region(endpoint['region'])
|
||||
except exception.RegionNotFound:
|
||||
region = dict(id=endpoint['region'])
|
||||
self.catalog_api.create_region(
|
||||
region, initiator=request.audit_initiator
|
||||
)
|
||||
|
||||
legacy_endpoint_ref = endpoint.copy()
|
||||
|
||||
urls = {}
|
||||
for i in INTERFACES:
|
||||
# remove all urls so they aren't persisted them more than once
|
||||
url = '%surl' % i
|
||||
if endpoint.get(url):
|
||||
# valid urls need to be persisted
|
||||
urls[i] = endpoint.pop(url)
|
||||
elif url in endpoint:
|
||||
# null or empty urls can be discarded
|
||||
endpoint.pop(url)
|
||||
legacy_endpoint_ref.pop(url)
|
||||
|
||||
legacy_endpoint_id = uuid.uuid4().hex
|
||||
for interface, url in urls.items():
|
||||
endpoint_ref = endpoint.copy()
|
||||
endpoint_ref['id'] = uuid.uuid4().hex
|
||||
endpoint_ref['legacy_endpoint_id'] = legacy_endpoint_id
|
||||
endpoint_ref['interface'] = interface
|
||||
endpoint_ref['url'] = url
|
||||
endpoint_ref['region_id'] = endpoint_ref.pop('region', None)
|
||||
self.catalog_api.create_endpoint(endpoint_ref['id'],
|
||||
endpoint_ref,
|
||||
initiator=request.audit_initiator)
|
||||
|
||||
legacy_endpoint_ref['id'] = legacy_endpoint_id
|
||||
return {'endpoint': legacy_endpoint_ref}
|
||||
|
||||
@controller.v2_deprecated
|
||||
def delete_endpoint(self, request, endpoint_id):
|
||||
"""Delete up to three v3 endpoint refs based on a legacy ref ID."""
|
||||
self.assert_admin(request)
|
||||
|
||||
deleted_at_least_one = False
|
||||
for endpoint in self.catalog_api.list_endpoints():
|
||||
if endpoint['legacy_endpoint_id'] == endpoint_id:
|
||||
self.catalog_api.delete_endpoint(
|
||||
endpoint['id'],
|
||||
initiator=request.audit_initiator
|
||||
)
|
||||
deleted_at_least_one = True
|
||||
|
||||
if not deleted_at_least_one:
|
||||
raise exception.EndpointNotFound(endpoint_id=endpoint_id)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class RegionV3(controller.V3Controller):
|
||||
collection_name = 'regions'
|
||||
|
@ -945,61 +945,6 @@ class V2Notifications(BaseNotificationTest):
|
||||
'role',
|
||||
cadftaxonomy.SECURITY_ROLE)
|
||||
|
||||
def test_service_and_endpoint(self):
|
||||
token = self.get_scoped_token()
|
||||
resp = self.admin_request(
|
||||
method='POST',
|
||||
path='/v2.0/OS-KSADM/services',
|
||||
body={
|
||||
'OS-KSADM:service': {
|
||||
'name': uuid.uuid4().hex,
|
||||
'type': uuid.uuid4().hex,
|
||||
'description': uuid.uuid4().hex,
|
||||
},
|
||||
},
|
||||
token=token,
|
||||
)
|
||||
service_id = resp.result.get('OS-KSADM:service').get('id')
|
||||
self._assert_initiator_data_is_set(CREATED_OPERATION,
|
||||
'service',
|
||||
cadftaxonomy.SECURITY_SERVICE)
|
||||
resp = self.admin_request(
|
||||
method='POST',
|
||||
path='/v2.0/endpoints',
|
||||
body={
|
||||
'endpoint': {
|
||||
'region': uuid.uuid4().hex,
|
||||
'service_id': service_id,
|
||||
'publicurl': uuid.uuid4().hex,
|
||||
'adminurl': uuid.uuid4().hex,
|
||||
'internalurl': uuid.uuid4().hex,
|
||||
},
|
||||
},
|
||||
token=token,
|
||||
)
|
||||
endpoint_id = resp.result.get('endpoint').get('id')
|
||||
self._assert_initiator_data_is_set(CREATED_OPERATION,
|
||||
'endpoint',
|
||||
cadftaxonomy.SECURITY_ENDPOINT)
|
||||
# test for delete endpoint
|
||||
self.admin_request(
|
||||
method='DELETE',
|
||||
path='/v2.0/endpoints/%s' % endpoint_id,
|
||||
token=token,
|
||||
)
|
||||
self._assert_initiator_data_is_set(DELETED_OPERATION,
|
||||
'endpoint',
|
||||
cadftaxonomy.SECURITY_ENDPOINT)
|
||||
# test for delete service
|
||||
self.admin_request(
|
||||
method='DELETE',
|
||||
path='/v2.0/OS-KSADM/services/%s' % service_id,
|
||||
token=token,
|
||||
)
|
||||
self._assert_initiator_data_is_set(DELETED_OPERATION,
|
||||
'service',
|
||||
cadftaxonomy.SECURITY_SERVICE)
|
||||
|
||||
def test_project(self):
|
||||
token = self.get_scoped_token()
|
||||
resp = self.admin_request(
|
||||
|
@ -1,365 +0,0 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# 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 uuid
|
||||
|
||||
from six.moves import http_client
|
||||
|
||||
from keystone import catalog
|
||||
from keystone.tests import unit
|
||||
from keystone.tests.unit.ksfixtures import database
|
||||
from keystone.tests.unit import rest
|
||||
|
||||
|
||||
BASE_URL = 'http://127.0.0.1:35357/v2'
|
||||
SERVICE_ID = uuid.uuid4().hex
|
||||
|
||||
|
||||
class V2CatalogTestCase(rest.RestfulTestCase):
|
||||
def setUp(self):
|
||||
super(V2CatalogTestCase, self).setUp()
|
||||
self.useFixture(database.Database())
|
||||
|
||||
self.service = unit.new_service_ref()
|
||||
self.service_id = self.service['id']
|
||||
self.catalog_api.create_service(self.service_id, self.service)
|
||||
|
||||
# TODO(termie): add an admin user to the fixtures and use that user
|
||||
# override the fixtures, for now
|
||||
self.assignment_api.add_role_to_user_and_project(
|
||||
self.user_foo['id'],
|
||||
self.tenant_bar['id'],
|
||||
self.role_admin['id'])
|
||||
|
||||
def config_overrides(self):
|
||||
super(V2CatalogTestCase, self).config_overrides()
|
||||
self.config_fixture.config(group='catalog', driver='sql')
|
||||
|
||||
def _get_token_id(self, r):
|
||||
"""Applicable only to JSON."""
|
||||
return r.result['access']['token']['id']
|
||||
|
||||
def _endpoint_create(self, expected_status=http_client.OK,
|
||||
service_id=SERVICE_ID,
|
||||
publicurl='http://localhost:8080',
|
||||
internalurl='http://localhost:8080',
|
||||
adminurl='http://localhost:8080',
|
||||
region=uuid.uuid4().hex):
|
||||
if service_id is SERVICE_ID:
|
||||
service_id = self.service_id
|
||||
|
||||
path = '/v2.0/endpoints'
|
||||
body = {
|
||||
'endpoint': {
|
||||
'adminurl': adminurl,
|
||||
'service_id': service_id,
|
||||
'internalurl': internalurl,
|
||||
'publicurl': publicurl
|
||||
}
|
||||
}
|
||||
if region is not None:
|
||||
body['endpoint']['region'] = region
|
||||
|
||||
r = self.admin_request(method='POST', token=self.get_scoped_token(),
|
||||
path=path, expected_status=expected_status,
|
||||
body=body)
|
||||
return body, r
|
||||
|
||||
def _region_create(self):
|
||||
region = unit.new_region_ref()
|
||||
region_id = region['id']
|
||||
self.catalog_api.create_region(region)
|
||||
return region_id
|
||||
|
||||
def test_endpoint_create(self):
|
||||
req_body, response = self._endpoint_create()
|
||||
self.assertIn('endpoint', response.result)
|
||||
self.assertIn('id', response.result['endpoint'])
|
||||
for field, value in req_body['endpoint'].items():
|
||||
self.assertEqual(value, response.result['endpoint'][field])
|
||||
|
||||
def test_endpoint_create_without_region(self):
|
||||
req_body, response = self._endpoint_create(region=None)
|
||||
self.assertIn('endpoint', response.result)
|
||||
self.assertIn('id', response.result['endpoint'])
|
||||
self.assertNotIn('region', response.result['endpoint'])
|
||||
for field, value in req_body['endpoint'].items():
|
||||
self.assertEqual(value, response.result['endpoint'][field])
|
||||
|
||||
def test_pure_v3_endpoint_with_publicurl_visible_from_v2(self):
|
||||
"""Test pure v3 endpoint can be fetched via v2.0 API.
|
||||
|
||||
For those who are using v2.0 APIs, endpoints created by v3 API should
|
||||
also be visible as there are no differences about the endpoints
|
||||
except the format or the internal implementation. Since publicURL is
|
||||
required for v2.0 API, so only v3 endpoints of the service which have
|
||||
the public interface endpoint will be converted into v2.0 endpoints.
|
||||
"""
|
||||
region_id = self._region_create()
|
||||
|
||||
# create v3 endpoints with three interfaces
|
||||
body = {
|
||||
'endpoint': unit.new_endpoint_ref(self.service_id,
|
||||
region_id=region_id)
|
||||
}
|
||||
for interface in catalog.controllers.INTERFACES:
|
||||
body['endpoint']['interface'] = interface
|
||||
self.admin_request(method='POST',
|
||||
token=self.get_scoped_token(),
|
||||
path='/v3/endpoints',
|
||||
expected_status=http_client.CREATED,
|
||||
body=body)
|
||||
|
||||
r = self.admin_request(token=self.get_scoped_token(),
|
||||
path='/v2.0/endpoints')
|
||||
# Endpoints of the service which have a public interface endpoint
|
||||
# will be returned via v2.0 API
|
||||
self.assertEqual(1, len(r.result['endpoints']))
|
||||
v2_endpoint = r.result['endpoints'][0]
|
||||
self.assertEqual(self.service_id, v2_endpoint['service_id'])
|
||||
# This is not the focus of this test, so no different urls are used.
|
||||
self.assertEqual(body['endpoint']['url'], v2_endpoint['publicurl'])
|
||||
self.assertEqual(body['endpoint']['url'], v2_endpoint['adminurl'])
|
||||
self.assertEqual(body['endpoint']['url'], v2_endpoint['internalurl'])
|
||||
self.assertNotIn('name', v2_endpoint)
|
||||
|
||||
v3_endpoint = self.catalog_api.get_endpoint(v2_endpoint['id'])
|
||||
# Checks the v3 public endpoint's id is the generated v2.0 endpoint
|
||||
self.assertEqual('public', v3_endpoint['interface'])
|
||||
self.assertEqual(self.service_id, v3_endpoint['service_id'])
|
||||
|
||||
def test_pure_v3_endpoint_without_publicurl_invisible_from_v2(self):
|
||||
"""Test that the v2.0 API can't fetch v3 endpoints without publicURLs.
|
||||
|
||||
v2.0 API will return endpoints created by v3 API, but publicURL is
|
||||
required for the service in the v2.0 API, therefore v3 endpoints of
|
||||
a service which don't have publicURL will be ignored.
|
||||
"""
|
||||
region_id = self._region_create()
|
||||
|
||||
# create a v3 endpoint without public interface
|
||||
body = {
|
||||
'endpoint': unit.new_endpoint_ref(self.service_id,
|
||||
region_id=region_id)
|
||||
}
|
||||
for interface in catalog.controllers.INTERFACES:
|
||||
if interface == 'public':
|
||||
continue
|
||||
body['endpoint']['interface'] = interface
|
||||
self.admin_request(method='POST',
|
||||
token=self.get_scoped_token(),
|
||||
path='/v3/endpoints',
|
||||
expected_status=http_client.CREATED,
|
||||
body=body)
|
||||
|
||||
r = self.admin_request(token=self.get_scoped_token(),
|
||||
path='/v2.0/endpoints')
|
||||
# v3 endpoints of a service which don't have publicURL can't be
|
||||
# fetched via v2.0 API
|
||||
self.assertEqual(0, len(r.result['endpoints']))
|
||||
|
||||
def test_endpoint_create_with_null_adminurl(self):
|
||||
req_body, response = self._endpoint_create(adminurl=None)
|
||||
self.assertIsNone(req_body['endpoint']['adminurl'])
|
||||
self.assertNotIn('adminurl', response.result['endpoint'])
|
||||
|
||||
def test_endpoint_create_with_empty_adminurl(self):
|
||||
req_body, response = self._endpoint_create(adminurl='')
|
||||
self.assertEqual('', req_body['endpoint']['adminurl'])
|
||||
self.assertNotIn("adminurl", response.result['endpoint'])
|
||||
|
||||
def test_endpoint_create_with_null_internalurl(self):
|
||||
req_body, response = self._endpoint_create(internalurl=None)
|
||||
self.assertIsNone(req_body['endpoint']['internalurl'])
|
||||
self.assertNotIn('internalurl', response.result['endpoint'])
|
||||
|
||||
def test_endpoint_create_with_empty_internalurl(self):
|
||||
req_body, response = self._endpoint_create(internalurl='')
|
||||
self.assertEqual('', req_body['endpoint']['internalurl'])
|
||||
self.assertNotIn("internalurl", response.result['endpoint'])
|
||||
|
||||
def test_endpoint_create_with_null_publicurl(self):
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
publicurl=None)
|
||||
|
||||
def test_endpoint_create_with_empty_publicurl(self):
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
publicurl='')
|
||||
|
||||
def test_endpoint_create_with_null_service_id(self):
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
service_id=None)
|
||||
|
||||
def test_endpoint_create_with_empty_service_id(self):
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
service_id='')
|
||||
|
||||
def test_endpoint_create_with_valid_url(self):
|
||||
"""Create endpoint with valid URL should be tested, too."""
|
||||
# list one valid url is enough, no need to list too much
|
||||
valid_url = 'http://127.0.0.1:8774/v1.1/$(tenant_id)s'
|
||||
|
||||
# baseline tests that all valid URLs works
|
||||
self._endpoint_create(expected_status=http_client.OK,
|
||||
publicurl=valid_url,
|
||||
internalurl=valid_url,
|
||||
adminurl=valid_url)
|
||||
|
||||
def test_endpoint_create_with_invalid_url(self):
|
||||
"""Test the invalid cases: substitutions is not exactly right."""
|
||||
invalid_urls = [
|
||||
# using a substitution that is not whitelisted - KeyError
|
||||
'http://127.0.0.1:8774/v1.1/$(nonexistent)s',
|
||||
|
||||
# invalid formatting - ValueError
|
||||
'http://127.0.0.1:8774/v1.1/$(project_id)',
|
||||
'http://127.0.0.1:8774/v1.1/$(project_id)t',
|
||||
'http://127.0.0.1:8774/v1.1/$(project_id',
|
||||
|
||||
# invalid type specifier - TypeError
|
||||
# admin_url is a string not an int
|
||||
'http://127.0.0.1:8774/v1.1/$(admin_url)d',
|
||||
]
|
||||
|
||||
# list one valid url is enough, no need to list too much
|
||||
valid_url = 'http://127.0.0.1:8774/v1.1/$(project_id)s'
|
||||
|
||||
# Case one: publicurl, internalurl and adminurl are
|
||||
# all invalid
|
||||
for invalid_url in invalid_urls:
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
publicurl=invalid_url,
|
||||
internalurl=invalid_url,
|
||||
adminurl=invalid_url)
|
||||
|
||||
# Case two: publicurl, internalurl are invalid
|
||||
# and adminurl is valid
|
||||
for invalid_url in invalid_urls:
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
publicurl=invalid_url,
|
||||
internalurl=invalid_url,
|
||||
adminurl=valid_url)
|
||||
|
||||
# Case three: publicurl, adminurl are invalid
|
||||
# and internalurl is valid
|
||||
for invalid_url in invalid_urls:
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
publicurl=invalid_url,
|
||||
internalurl=valid_url,
|
||||
adminurl=invalid_url)
|
||||
|
||||
# Case four: internalurl, adminurl are invalid
|
||||
# and publicurl is valid
|
||||
for invalid_url in invalid_urls:
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
publicurl=valid_url,
|
||||
internalurl=invalid_url,
|
||||
adminurl=invalid_url)
|
||||
|
||||
# Case five: publicurl is invalid, internalurl
|
||||
# and adminurl are valid
|
||||
for invalid_url in invalid_urls:
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
publicurl=invalid_url,
|
||||
internalurl=valid_url,
|
||||
adminurl=valid_url)
|
||||
|
||||
# Case six: internalurl is invalid, publicurl
|
||||
# and adminurl are valid
|
||||
for invalid_url in invalid_urls:
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
publicurl=valid_url,
|
||||
internalurl=invalid_url,
|
||||
adminurl=valid_url)
|
||||
|
||||
# Case seven: adminurl is invalid, publicurl
|
||||
# and internalurl are valid
|
||||
for invalid_url in invalid_urls:
|
||||
self._endpoint_create(expected_status=http_client.BAD_REQUEST,
|
||||
publicurl=valid_url,
|
||||
internalurl=valid_url,
|
||||
adminurl=invalid_url)
|
||||
|
||||
|
||||
class TestV2CatalogAPISQL(unit.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestV2CatalogAPISQL, self).setUp()
|
||||
self.useFixture(database.Database())
|
||||
self.catalog_api = catalog.Manager()
|
||||
|
||||
service = unit.new_service_ref()
|
||||
self.service_id = service['id']
|
||||
self.catalog_api.create_service(self.service_id, service)
|
||||
|
||||
self.create_endpoint(service_id=self.service_id)
|
||||
|
||||
def create_endpoint(self, service_id, **kwargs):
|
||||
endpoint = unit.new_endpoint_ref(service_id=service_id,
|
||||
region_id=None,
|
||||
**kwargs)
|
||||
self.catalog_api.create_endpoint(endpoint['id'], endpoint)
|
||||
return endpoint
|
||||
|
||||
def config_overrides(self):
|
||||
super(TestV2CatalogAPISQL, self).config_overrides()
|
||||
self.config_fixture.config(group='catalog', driver='sql')
|
||||
|
||||
def test_get_catalog_ignores_endpoints_with_invalid_urls(self):
|
||||
user_id = uuid.uuid4().hex
|
||||
project_id = uuid.uuid4().hex
|
||||
|
||||
# the only endpoint in the catalog is the one created in setUp
|
||||
catalog = self.catalog_api.get_catalog(user_id, project_id)
|
||||
self.assertEqual(1, len(catalog))
|
||||
# it's also the only endpoint in the backend
|
||||
self.assertEqual(1, len(self.catalog_api.list_endpoints()))
|
||||
|
||||
# create a new, invalid endpoint - malformed type declaration
|
||||
self.create_endpoint(self.service_id,
|
||||
url='http://keystone/%(project_id)')
|
||||
|
||||
# create a new, invalid endpoint - nonexistent key
|
||||
self.create_endpoint(self.service_id,
|
||||
url='http://keystone/%(you_wont_find_me)s')
|
||||
|
||||
# verify that the invalid endpoints don't appear in the catalog
|
||||
catalog = self.catalog_api.get_catalog(user_id, project_id)
|
||||
self.assertEqual(1, len(catalog))
|
||||
# all three endpoints appear in the backend
|
||||
self.assertEqual(3, len(self.catalog_api.list_endpoints()))
|
||||
|
||||
def test_get_catalog_always_returns_service_name(self):
|
||||
user_id = uuid.uuid4().hex
|
||||
project_id = uuid.uuid4().hex
|
||||
|
||||
# new_service_ref() returns a ref with a `name`.
|
||||
named_svc = unit.new_service_ref()
|
||||
self.catalog_api.create_service(named_svc['id'], named_svc)
|
||||
self.create_endpoint(service_id=named_svc['id'])
|
||||
|
||||
# This time manually delete the generated `name`.
|
||||
unnamed_svc = unit.new_service_ref()
|
||||
del unnamed_svc['name']
|
||||
self.catalog_api.create_service(unnamed_svc['id'], unnamed_svc)
|
||||
self.create_endpoint(service_id=unnamed_svc['id'])
|
||||
|
||||
region = None
|
||||
catalog = self.catalog_api.get_catalog(user_id, project_id)
|
||||
|
||||
self.assertEqual(named_svc['name'],
|
||||
catalog[region][named_svc['type']]['name'])
|
||||
|
||||
# verify a name is not generated when the service is passed to the API
|
||||
self.assertEqual('', catalog[region][unnamed_svc['type']]['name'])
|
@ -385,27 +385,6 @@ class CoreApiTests(object):
|
||||
self.public_request(path='/v2.0/tenants',
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
def test_invalid_parameter_error_response(self):
|
||||
token = self.get_scoped_token()
|
||||
bad_body = {
|
||||
'OS-KSADM:service%s' % uuid.uuid4().hex: {
|
||||
'name': uuid.uuid4().hex,
|
||||
'type': uuid.uuid4().hex,
|
||||
},
|
||||
}
|
||||
res = self.admin_request(method='POST',
|
||||
path='/v2.0/OS-KSADM/services',
|
||||
body=bad_body,
|
||||
token=token,
|
||||
expected_status=http_client.BAD_REQUEST)
|
||||
self.assertValidErrorResponse(res)
|
||||
res = self.admin_request(method='POST',
|
||||
path='/v2.0/users',
|
||||
body=bad_body,
|
||||
token=token,
|
||||
expected_status=http_client.BAD_REQUEST)
|
||||
self.assertValidErrorResponse(res)
|
||||
|
||||
def _get_user_id(self, r):
|
||||
"""Helper method to return user ID from a response.
|
||||
|
||||
@ -1158,39 +1137,6 @@ class V2TestCase(object):
|
||||
def get_user_attribute_from_response(self, r, attribute_name):
|
||||
return r.result['user'][attribute_name]
|
||||
|
||||
def test_service_crud_requires_auth(self):
|
||||
"""Service CRUD should return unauthorized without an X-Auth-Token."""
|
||||
# values here don't matter because it will be unauthorized before
|
||||
# they're checked (bug 1006822).
|
||||
service_path = '/v2.0/OS-KSADM/services/%s' % uuid.uuid4().hex
|
||||
service_body = {
|
||||
'OS-KSADM:service': {
|
||||
'name': uuid.uuid4().hex,
|
||||
'type': uuid.uuid4().hex,
|
||||
},
|
||||
}
|
||||
|
||||
r = self.admin_request(method='GET',
|
||||
path='/v2.0/OS-KSADM/services',
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
self.assertValidErrorResponse(r)
|
||||
|
||||
r = self.admin_request(method='POST',
|
||||
path='/v2.0/OS-KSADM/services',
|
||||
body=service_body,
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
self.assertValidErrorResponse(r)
|
||||
|
||||
r = self.admin_request(method='GET',
|
||||
path=service_path,
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
self.assertValidErrorResponse(r)
|
||||
|
||||
r = self.admin_request(method='DELETE',
|
||||
path=service_path,
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
self.assertValidErrorResponse(r)
|
||||
|
||||
def test_user_role_list_requires_auth(self):
|
||||
"""User role list return unauthorized without an X-Auth-Token."""
|
||||
# values here don't matter because it will be unauthorized before
|
||||
|
@ -677,58 +677,6 @@ class CatalogTestCase(test_v3.RestfulTestCase):
|
||||
'/endpoints/%(endpoint_id)s' % {
|
||||
'endpoint_id': self.endpoint_id})
|
||||
|
||||
def test_create_endpoint_on_v2(self):
|
||||
# clear the v3 endpoint so we only have endpoints created on v2
|
||||
self.delete(
|
||||
'/endpoints/%(endpoint_id)s' % {
|
||||
'endpoint_id': self.endpoint_id})
|
||||
|
||||
# create a v3 endpoint ref, and then tweak it back to a v2-style ref
|
||||
ref = unit.new_endpoint_ref_with_region(service_id=self.service['id'],
|
||||
region=uuid.uuid4().hex,
|
||||
internalurl=None)
|
||||
del ref['id']
|
||||
del ref['interface']
|
||||
ref['publicurl'] = ref.pop('url')
|
||||
# don't set adminurl to ensure it's absence is handled like internalurl
|
||||
|
||||
# create the endpoint on v2 (using a v3 token)
|
||||
r = self.admin_request(
|
||||
method='POST',
|
||||
path='/v2.0/endpoints',
|
||||
token=self.get_scoped_token(),
|
||||
body={'endpoint': ref})
|
||||
endpoint_v2 = r.result['endpoint']
|
||||
|
||||
# test the endpoint on v3
|
||||
r = self.get('/endpoints')
|
||||
endpoints = self.assertValidEndpointListResponse(r)
|
||||
self.assertEqual(1, len(endpoints))
|
||||
endpoint_v3 = endpoints.pop()
|
||||
|
||||
# these attributes are identical between both APIs
|
||||
self.assertEqual(ref['region'], endpoint_v3['region_id'])
|
||||
self.assertEqual(ref['service_id'], endpoint_v3['service_id'])
|
||||
self.assertEqual(ref['description'], endpoint_v3['description'])
|
||||
|
||||
# a v2 endpoint is not quite the same concept as a v3 endpoint, so they
|
||||
# receive different identifiers
|
||||
self.assertNotEqual(endpoint_v2['id'], endpoint_v3['id'])
|
||||
|
||||
# v2 has a publicurl; v3 has a url + interface type
|
||||
self.assertEqual(ref['publicurl'], endpoint_v3['url'])
|
||||
self.assertEqual('public', endpoint_v3['interface'])
|
||||
|
||||
# tests for bug 1152632 -- these attributes were being returned by v3
|
||||
self.assertNotIn('publicurl', endpoint_v3)
|
||||
self.assertNotIn('adminurl', endpoint_v3)
|
||||
self.assertNotIn('internalurl', endpoint_v3)
|
||||
|
||||
# test for bug 1152635 -- this attribute was being returned by v3
|
||||
self.assertNotIn('legacy_endpoint_id', endpoint_v3)
|
||||
|
||||
self.assertEqual(endpoint_v2['region'], endpoint_v3['region_id'])
|
||||
|
||||
def test_deleting_endpoint_with_space_in_url(self):
|
||||
# add a space to all urls (intentional "i d" to test bug)
|
||||
url_with_space = "http://127.0.0.1:8774 /v1.1/\$(tenant_i d)s"
|
||||
|
@ -13,7 +13,6 @@
|
||||
# under the License.
|
||||
|
||||
from keystone import assignment
|
||||
from keystone import catalog
|
||||
from keystone.common import extension
|
||||
from keystone.common import wsgi
|
||||
from keystone import identity
|
||||
@ -53,8 +52,6 @@ class Router(wsgi.ComposableRouter):
|
||||
user_controller = identity.controllers.User()
|
||||
role_controller = assignment.controllers.Role()
|
||||
assignment_role_controller = assignment.controllers.RoleAssignmentV2()
|
||||
service_controller = catalog.controllers.Service()
|
||||
endpoint_controller = catalog.controllers.Endpoint()
|
||||
|
||||
# Tenant Operations
|
||||
mapper.connect(
|
||||
@ -178,45 +175,6 @@ class Router(wsgi.ComposableRouter):
|
||||
action='remove_role_from_user',
|
||||
conditions=dict(method=['DELETE']))
|
||||
|
||||
# Service Operations
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services',
|
||||
controller=service_controller,
|
||||
action='get_services',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services',
|
||||
controller=service_controller,
|
||||
action='create_service',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services/{service_id}',
|
||||
controller=service_controller,
|
||||
action='delete_service',
|
||||
conditions=dict(method=['DELETE']))
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services/{service_id}',
|
||||
controller=service_controller,
|
||||
action='get_service',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# Endpoint Templates
|
||||
mapper.connect(
|
||||
'/endpoints',
|
||||
controller=endpoint_controller,
|
||||
action='get_endpoints',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect(
|
||||
'/endpoints',
|
||||
controller=endpoint_controller,
|
||||
action='create_endpoint',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect(
|
||||
'/endpoints/{endpoint_id}',
|
||||
controller=endpoint_controller,
|
||||
action='delete_endpoint',
|
||||
conditions=dict(method=['DELETE']))
|
||||
|
||||
# Role Operations
|
||||
mapper.connect(
|
||||
'/OS-KSADM/roles',
|
||||
|
Loading…
Reference in New Issue
Block a user