keystone/keystone/tests/unit/test_v3_catalog.py
wangxiyuan 012dac29b8 Enable foreign keys for unit test
The unit test uses sqlite for test which closes db foreign keys
function by default. This patch enabled the sqlite foreign keys
function for unit test by default.

The "project" table is a self referencing FK table(id <-> domain_id
column). So when the FK is enabled, there must exists a root record
before insert data to this table. It's <<keystone.domain.root>>.

Usually, the <<keystone.domain.root>> recored is inserted into the
table once operators run "keystone-manage db_sync" command when
deploy Keystone. But the unit test code doesn't run this command,
it initialise the db schema by reading sqlalchemy object model, so
the <<keystone.domain.root>> record is missed. Then we can't create
any project record, it'll raise FK error.

So in this patch, before creating any projects in the test, we must
ensure the <<keystone.domain.root>> record exists first.

Change-Id: I565d12395ca39a58ba90faf8641a9e02d986aeb9
Closes-Bug: #1744195
2018-10-09 09:50:21 +08:00

1027 lines
42 KiB
Python

# 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 copy
import uuid
from six.moves import http_client
from testtools import matchers
from keystone.common import provider_api
from keystone.tests import unit
from keystone.tests.unit import default_fixtures
from keystone.tests.unit.ksfixtures import database
from keystone.tests.unit import test_v3
PROVIDERS = provider_api.ProviderAPIs
class CatalogTestCase(test_v3.RestfulTestCase):
"""Test service & endpoint CRUD."""
# region crud tests
def test_create_region_with_id(self):
"""Call ``PUT /regions/{region_id}`` w/o an ID in the request body."""
ref = unit.new_region_ref()
region_id = ref.pop('id')
r = self.put(
'/regions/%s' % region_id,
body={'region': ref},
expected_status=http_client.CREATED)
self.assertValidRegionResponse(r, ref)
# Double-check that the region ID was kept as-is and not
# populated with a UUID, as is the case with POST /v3/regions
self.assertEqual(region_id, r.json['region']['id'])
def test_create_region_with_matching_ids(self):
"""Call ``PUT /regions/{region_id}`` with an ID in the request body."""
ref = unit.new_region_ref()
region_id = ref['id']
r = self.put(
'/regions/%s' % region_id,
body={'region': ref},
expected_status=http_client.CREATED)
self.assertValidRegionResponse(r, ref)
# Double-check that the region ID was kept as-is and not
# populated with a UUID, as is the case with POST /v3/regions
self.assertEqual(region_id, r.json['region']['id'])
def test_create_region_with_duplicate_id(self):
"""Call ``PUT /regions/{region_id}``."""
ref = unit.new_region_ref()
region_id = ref['id']
self.put(
'/regions/%s' % region_id,
body={'region': ref}, expected_status=http_client.CREATED)
# Create region again with duplicate id
self.put(
'/regions/%s' % region_id,
body={'region': ref}, expected_status=http_client.CONFLICT)
def test_create_region(self):
"""Call ``POST /regions`` with an ID in the request body."""
# the ref will have an ID defined on it
ref = unit.new_region_ref()
r = self.post(
'/regions',
body={'region': ref})
self.assertValidRegionResponse(r, ref)
# we should be able to get the region, having defined the ID ourselves
r = self.get(
'/regions/%(region_id)s' % {
'region_id': ref['id']})
self.assertValidRegionResponse(r, ref)
def test_create_region_with_empty_id(self):
"""Call ``POST /regions`` with an empty ID in the request body."""
ref = unit.new_region_ref(id='')
r = self.post('/regions', body={'region': ref})
self.assertValidRegionResponse(r, ref)
self.assertNotEmpty(r.result['region'].get('id'))
def test_create_region_without_id(self):
"""Call ``POST /regions`` without an ID in the request body."""
ref = unit.new_region_ref()
# instead of defining the ID ourselves...
del ref['id']
# let the service define the ID
r = self.post('/regions', body={'region': ref})
self.assertValidRegionResponse(r, ref)
def test_create_region_without_description(self):
"""Call ``POST /regions`` without description in the request body."""
ref = unit.new_region_ref(description=None)
del ref['description']
r = self.post('/regions', body={'region': ref})
# Create the description in the reference to compare to since the
# response should now have a description, even though we didn't send
# it with the original reference.
ref['description'] = ''
self.assertValidRegionResponse(r, ref)
def test_create_regions_with_same_description_string(self):
"""Call ``POST /regions`` with duplicate descriptions."""
# NOTE(lbragstad): Make sure we can create two regions that have the
# same description.
region_desc = 'Some Region Description'
ref1 = unit.new_region_ref(description=region_desc)
ref2 = unit.new_region_ref(description=region_desc)
resp1 = self.post('/regions', body={'region': ref1})
self.assertValidRegionResponse(resp1, ref1)
resp2 = self.post('/regions', body={'region': ref2})
self.assertValidRegionResponse(resp2, ref2)
def test_create_regions_without_descriptions(self):
"""Call ``POST /regions`` with no description."""
# NOTE(lbragstad): Make sure we can create two regions that have
# no description in the request body. The description should be
# populated by Catalog Manager.
ref1 = unit.new_region_ref()
ref2 = unit.new_region_ref()
del ref1['description']
ref2['description'] = None
resp1 = self.post('/regions', body={'region': ref1})
resp2 = self.post('/regions', body={'region': ref2})
# Create the descriptions in the references to compare to since the
# responses should now have descriptions, even though we didn't send
# a description with the original references.
ref1['description'] = ''
ref2['description'] = ''
self.assertValidRegionResponse(resp1, ref1)
self.assertValidRegionResponse(resp2, ref2)
def test_create_region_with_conflicting_ids(self):
"""Call ``PUT /regions/{region_id}`` with conflicting region IDs."""
# the region ref is created with an ID
ref = unit.new_region_ref()
# but instead of using that ID, make up a new, conflicting one
self.put(
'/regions/%s' % uuid.uuid4().hex,
body={'region': ref},
expected_status=http_client.BAD_REQUEST)
def test_list_head_regions(self):
"""Call ``GET & HEAD /regions``."""
resource_url = '/regions'
r = self.get(resource_url)
self.assertValidRegionListResponse(r, ref=self.region)
self.head(resource_url, expected_status=http_client.OK)
def _create_region_with_parent_id(self, parent_id=None):
ref = unit.new_region_ref(parent_region_id=parent_id)
return self.post(
'/regions',
body={'region': ref})
def test_list_regions_filtered_by_parent_region_id(self):
"""Call ``GET /regions?parent_region_id={parent_region_id}``."""
new_region = self._create_region_with_parent_id()
parent_id = new_region.result['region']['id']
new_region = self._create_region_with_parent_id(parent_id)
new_region = self._create_region_with_parent_id(parent_id)
r = self.get('/regions?parent_region_id=%s' % parent_id)
for region in r.result['regions']:
self.assertEqual(parent_id, region['parent_region_id'])
def test_get_head_region(self):
"""Call ``GET & HEAD /regions/{region_id}``."""
resource_url = '/regions/%(region_id)s' % {
'region_id': self.region_id}
r = self.get(resource_url)
self.assertValidRegionResponse(r, self.region)
self.head(resource_url, expected_status=http_client.OK)
def test_update_region(self):
"""Call ``PATCH /regions/{region_id}``."""
region = unit.new_region_ref()
del region['id']
r = self.patch('/regions/%(region_id)s' % {
'region_id': self.region_id},
body={'region': region})
self.assertValidRegionResponse(r, region)
def test_update_region_without_description_keeps_original(self):
"""Call ``PATCH /regions/{region_id}``."""
region_ref = unit.new_region_ref()
resp = self.post('/regions', body={'region': region_ref})
region_updates = {
# update with something that's not the description
'parent_region_id': self.region_id,
}
resp = self.patch('/regions/%s' % region_ref['id'],
body={'region': region_updates})
# NOTE(dstanek): Keystone should keep the original description.
self.assertEqual(region_ref['description'],
resp.result['region']['description'])
def test_update_region_with_null_description(self):
"""Call ``PATCH /regions/{region_id}``."""
region = unit.new_region_ref(description=None)
del region['id']
r = self.patch('/regions/%(region_id)s' % {
'region_id': self.region_id},
body={'region': region})
# NOTE(dstanek): Keystone should turn the provided None value into
# an empty string before storing in the backend.
region['description'] = ''
self.assertValidRegionResponse(r, region)
def test_delete_region(self):
"""Call ``DELETE /regions/{region_id}``."""
ref = unit.new_region_ref()
r = self.post(
'/regions',
body={'region': ref})
self.assertValidRegionResponse(r, ref)
self.delete('/regions/%(region_id)s' % {
'region_id': ref['id']})
# service crud tests
def test_create_service(self):
"""Call ``POST /services``."""
ref = unit.new_service_ref()
r = self.post(
'/services',
body={'service': ref})
self.assertValidServiceResponse(r, ref)
def test_create_service_no_name(self):
"""Call ``POST /services``."""
ref = unit.new_service_ref()
del ref['name']
r = self.post(
'/services',
body={'service': ref})
ref['name'] = ''
self.assertValidServiceResponse(r, ref)
def test_create_service_no_enabled(self):
"""Call ``POST /services``."""
ref = unit.new_service_ref()
del ref['enabled']
r = self.post(
'/services',
body={'service': ref})
ref['enabled'] = True
self.assertValidServiceResponse(r, ref)
self.assertIs(True, r.result['service']['enabled'])
def test_create_service_enabled_false(self):
"""Call ``POST /services``."""
ref = unit.new_service_ref(enabled=False)
r = self.post(
'/services',
body={'service': ref})
self.assertValidServiceResponse(r, ref)
self.assertIs(False, r.result['service']['enabled'])
def test_create_service_enabled_true(self):
"""Call ``POST /services``."""
ref = unit.new_service_ref(enabled=True)
r = self.post(
'/services',
body={'service': ref})
self.assertValidServiceResponse(r, ref)
self.assertIs(True, r.result['service']['enabled'])
def test_create_service_enabled_str_true(self):
"""Call ``POST /services``."""
ref = unit.new_service_ref(enabled='True')
self.post('/services', body={'service': ref},
expected_status=http_client.BAD_REQUEST)
def test_create_service_enabled_str_false(self):
"""Call ``POST /services``."""
ref = unit.new_service_ref(enabled='False')
self.post('/services', body={'service': ref},
expected_status=http_client.BAD_REQUEST)
def test_create_service_enabled_str_random(self):
"""Call ``POST /services``."""
ref = unit.new_service_ref(enabled='puppies')
self.post('/services', body={'service': ref},
expected_status=http_client.BAD_REQUEST)
def test_list_head_services(self):
"""Call ``GET & HEAD /services``."""
resource_url = '/services'
r = self.get(resource_url)
self.assertValidServiceListResponse(r, ref=self.service)
self.head(resource_url, expected_status=http_client.OK)
def _create_random_service(self):
ref = unit.new_service_ref()
response = self.post(
'/services',
body={'service': ref})
return response.json['service']
def test_filter_list_services_by_type(self):
"""Call ``GET /services?type=<some type>``."""
target_ref = self._create_random_service()
# create unrelated services
self._create_random_service()
self._create_random_service()
response = self.get('/services?type=' + target_ref['type'])
self.assertValidServiceListResponse(response, ref=target_ref)
filtered_service_list = response.json['services']
self.assertEqual(1, len(filtered_service_list))
filtered_service = filtered_service_list[0]
self.assertEqual(target_ref['type'], filtered_service['type'])
def test_filter_list_services_by_name(self):
"""Call ``GET /services?name=<some name>``."""
# create unrelated services
self._create_random_service()
self._create_random_service()
# create the desired service
target_ref = self._create_random_service()
response = self.get('/services?name=' + target_ref['name'])
self.assertValidServiceListResponse(response, ref=target_ref)
filtered_service_list = response.json['services']
self.assertEqual(1, len(filtered_service_list))
filtered_service = filtered_service_list[0]
self.assertEqual(target_ref['name'], filtered_service['name'])
def test_filter_list_services_by_name_with_list_limit(self):
"""Call ``GET /services?name=<some name>``."""
self.config_fixture.config(list_limit=1)
self.test_filter_list_services_by_name()
def test_get_head_service(self):
"""Call ``GET & HEAD /services/{service_id}``."""
resource_url = '/services/%(service_id)s' % {
'service_id': self.service_id}
r = self.get(resource_url)
self.assertValidServiceResponse(r, self.service)
self.head(resource_url, expected_status=http_client.OK)
def test_update_service(self):
"""Call ``PATCH /services/{service_id}``."""
service = unit.new_service_ref()
del service['id']
r = self.patch('/services/%(service_id)s' % {
'service_id': self.service_id},
body={'service': service})
self.assertValidServiceResponse(r, service)
def test_delete_service(self):
"""Call ``DELETE /services/{service_id}``."""
self.delete('/services/%(service_id)s' % {
'service_id': self.service_id})
# endpoint crud tests
def test_list_head_endpoints(self):
"""Call ``GET & HEAD /endpoints``."""
resource_url = '/endpoints'
r = self.get(resource_url)
self.assertValidEndpointListResponse(r, ref=self.endpoint)
self.head(resource_url, expected_status=http_client.OK)
def _create_random_endpoint(self, interface='public',
parent_region_id=None):
region = self._create_region_with_parent_id(
parent_id=parent_region_id)
service = self._create_random_service()
ref = unit.new_endpoint_ref(
service_id=service['id'],
interface=interface,
region_id=region.result['region']['id'])
response = self.post(
'/endpoints',
body={'endpoint': ref})
return response.json['endpoint']
def test_list_endpoints_filtered_by_interface(self):
"""Call ``GET /endpoints?interface={interface}``."""
ref = self._create_random_endpoint(interface='internal')
response = self.get('/endpoints?interface=%s' % ref['interface'])
self.assertValidEndpointListResponse(response, ref=ref)
for endpoint in response.json['endpoints']:
self.assertEqual(ref['interface'], endpoint['interface'])
def test_list_endpoints_filtered_by_service_id(self):
"""Call ``GET /endpoints?service_id={service_id}``."""
ref = self._create_random_endpoint()
response = self.get('/endpoints?service_id=%s' % ref['service_id'])
self.assertValidEndpointListResponse(response, ref=ref)
for endpoint in response.json['endpoints']:
self.assertEqual(ref['service_id'], endpoint['service_id'])
def test_list_endpoints_filtered_by_region_id(self):
"""Call ``GET /endpoints?region_id={region_id}``."""
ref = self._create_random_endpoint()
response = self.get('/endpoints?region_id=%s' % ref['region_id'])
self.assertValidEndpointListResponse(response, ref=ref)
for endpoint in response.json['endpoints']:
self.assertEqual(ref['region_id'], endpoint['region_id'])
def test_list_endpoints_filtered_by_parent_region_id(self):
"""Call ``GET /endpoints?region_id={region_id}``.
Ensure passing the parent_region_id as filter returns an
empty list.
"""
parent_region = self._create_region_with_parent_id()
parent_region_id = parent_region.result['region']['id']
self._create_random_endpoint(parent_region_id=parent_region_id)
response = self.get('/endpoints?region_id=%s' % parent_region_id)
self.assertEqual(0, len(response.json['endpoints']))
def test_list_endpoints_with_multiple_filters(self):
"""Call ``GET /endpoints?interface={interface}...``.
Ensure passing different combinations of interface, region_id and
service_id as filters will return the correct result.
"""
# interface and region_id specified
ref = self._create_random_endpoint(interface='internal')
response = self.get('/endpoints?interface=%s&region_id=%s' %
(ref['interface'], ref['region_id']))
self.assertValidEndpointListResponse(response, ref=ref)
for endpoint in response.json['endpoints']:
self.assertEqual(ref['interface'], endpoint['interface'])
self.assertEqual(ref['region_id'], endpoint['region_id'])
# interface and service_id specified
ref = self._create_random_endpoint(interface='internal')
response = self.get('/endpoints?interface=%s&service_id=%s' %
(ref['interface'], ref['service_id']))
self.assertValidEndpointListResponse(response, ref=ref)
for endpoint in response.json['endpoints']:
self.assertEqual(ref['interface'], endpoint['interface'])
self.assertEqual(ref['service_id'], endpoint['service_id'])
# region_id and service_id specified
ref = self._create_random_endpoint(interface='internal')
response = self.get('/endpoints?region_id=%s&service_id=%s' %
(ref['region_id'], ref['service_id']))
self.assertValidEndpointListResponse(response, ref=ref)
for endpoint in response.json['endpoints']:
self.assertEqual(ref['region_id'], endpoint['region_id'])
self.assertEqual(ref['service_id'], endpoint['service_id'])
# interface, region_id and service_id specified
ref = self._create_random_endpoint(interface='internal')
response = self.get(('/endpoints?interface=%s&region_id=%s'
'&service_id=%s') %
(ref['interface'], ref['region_id'],
ref['service_id']))
self.assertValidEndpointListResponse(response, ref=ref)
for endpoint in response.json['endpoints']:
self.assertEqual(ref['interface'], endpoint['interface'])
self.assertEqual(ref['region_id'], endpoint['region_id'])
self.assertEqual(ref['service_id'], endpoint['service_id'])
def test_list_endpoints_with_random_filter_values(self):
"""Call ``GET /endpoints?interface={interface}...``.
Ensure passing random values for: interface, region_id and
service_id will return an empty list.
"""
self._create_random_endpoint(interface='internal')
response = self.get('/endpoints?interface=%s' % uuid.uuid4().hex)
self.assertEqual(0, len(response.json['endpoints']))
response = self.get('/endpoints?region_id=%s' % uuid.uuid4().hex)
self.assertEqual(0, len(response.json['endpoints']))
response = self.get('/endpoints?service_id=%s' % uuid.uuid4().hex)
self.assertEqual(0, len(response.json['endpoints']))
def test_create_endpoint_no_enabled(self):
"""Call ``POST /endpoints``."""
ref = unit.new_endpoint_ref(service_id=self.service_id,
interface='public',
region_id=self.region_id)
r = self.post('/endpoints', body={'endpoint': ref})
ref['enabled'] = True
self.assertValidEndpointResponse(r, ref)
def test_create_endpoint_enabled_true(self):
"""Call ``POST /endpoints`` with enabled: true."""
ref = unit.new_endpoint_ref(service_id=self.service_id,
interface='public',
region_id=self.region_id,
enabled=True)
r = self.post('/endpoints', body={'endpoint': ref})
self.assertValidEndpointResponse(r, ref)
def test_create_endpoint_enabled_false(self):
"""Call ``POST /endpoints`` with enabled: false."""
ref = unit.new_endpoint_ref(service_id=self.service_id,
interface='public',
region_id=self.region_id,
enabled=False)
r = self.post('/endpoints', body={'endpoint': ref})
self.assertValidEndpointResponse(r, ref)
def test_create_endpoint_enabled_str_true(self):
"""Call ``POST /endpoints`` with enabled: 'True'."""
ref = unit.new_endpoint_ref(service_id=self.service_id,
interface='public',
region_id=self.region_id,
enabled='True')
self.post('/endpoints', body={'endpoint': ref},
expected_status=http_client.BAD_REQUEST)
def test_create_endpoint_enabled_str_false(self):
"""Call ``POST /endpoints`` with enabled: 'False'."""
ref = unit.new_endpoint_ref(service_id=self.service_id,
interface='public',
region_id=self.region_id,
enabled='False')
self.post('/endpoints', body={'endpoint': ref},
expected_status=http_client.BAD_REQUEST)
def test_create_endpoint_enabled_str_random(self):
"""Call ``POST /endpoints`` with enabled: 'puppies'."""
ref = unit.new_endpoint_ref(service_id=self.service_id,
interface='public',
region_id=self.region_id,
enabled='puppies')
self.post('/endpoints', body={'endpoint': ref},
expected_status=http_client.BAD_REQUEST)
def test_create_endpoint_with_invalid_region_id(self):
"""Call ``POST /endpoints``."""
ref = unit.new_endpoint_ref(service_id=self.service_id)
self.post('/endpoints', body={'endpoint': ref},
expected_status=http_client.BAD_REQUEST)
def test_create_endpoint_with_region(self):
"""EndpointV3 creates the region before creating the endpoint.
This occurs when endpoint is provided with 'region' and no 'region_id'.
"""
ref = unit.new_endpoint_ref_with_region(service_id=self.service_id,
region=uuid.uuid4().hex)
self.post('/endpoints', body={'endpoint': ref})
# Make sure the region is created
self.get('/regions/%(region_id)s' % {'region_id': ref["region"]})
def test_create_endpoint_with_no_region(self):
"""EndpointV3 allows to creates the endpoint without region."""
ref = unit.new_endpoint_ref(service_id=self.service_id, region_id=None)
del ref['region_id'] # cannot just be None, it needs to not exist
self.post('/endpoints', body={'endpoint': ref})
def test_create_endpoint_with_empty_url(self):
"""Call ``POST /endpoints``."""
ref = unit.new_endpoint_ref(service_id=self.service_id, url='')
self.post('/endpoints', body={'endpoint': ref},
expected_status=http_client.BAD_REQUEST)
def test_get_head_endpoint(self):
"""Call ``GET & HEAD /endpoints/{endpoint_id}``."""
resource_url = '/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id}
r = self.get(resource_url)
self.assertValidEndpointResponse(r, self.endpoint)
self.head(resource_url, expected_status=http_client.OK)
def test_update_endpoint(self):
"""Call ``PATCH /endpoints/{endpoint_id}``."""
ref = unit.new_endpoint_ref(service_id=self.service_id,
interface='public',
region_id=self.region_id)
del ref['id']
r = self.patch(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id},
body={'endpoint': ref})
ref['enabled'] = True
self.assertValidEndpointResponse(r, ref)
def test_update_endpoint_enabled_true(self):
"""Call ``PATCH /endpoints/{endpoint_id}`` with enabled: True."""
r = self.patch(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id},
body={'endpoint': {'enabled': True}})
self.assertValidEndpointResponse(r, self.endpoint)
def test_update_endpoint_enabled_false(self):
"""Call ``PATCH /endpoints/{endpoint_id}`` with enabled: False."""
r = self.patch(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id},
body={'endpoint': {'enabled': False}})
exp_endpoint = copy.copy(self.endpoint)
exp_endpoint['enabled'] = False
self.assertValidEndpointResponse(r, exp_endpoint)
def test_update_endpoint_enabled_str_true(self):
"""Call ``PATCH /endpoints/{endpoint_id}`` with enabled: 'True'."""
self.patch(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id},
body={'endpoint': {'enabled': 'True'}},
expected_status=http_client.BAD_REQUEST)
def test_update_endpoint_enabled_str_false(self):
"""Call ``PATCH /endpoints/{endpoint_id}`` with enabled: 'False'."""
self.patch(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id},
body={'endpoint': {'enabled': 'False'}},
expected_status=http_client.BAD_REQUEST)
def test_update_endpoint_enabled_str_random(self):
"""Call ``PATCH /endpoints/{endpoint_id}`` with enabled: 'kitties'."""
self.patch(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_id},
body={'endpoint': {'enabled': 'kitties'}},
expected_status=http_client.BAD_REQUEST)
def test_delete_endpoint(self):
"""Call ``DELETE /endpoints/{endpoint_id}``."""
self.delete(
'/endpoints/%(endpoint_id)s' % {
'endpoint_id': self.endpoint_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"
# create a v3 endpoint ref
ref = unit.new_endpoint_ref(service_id=self.service['id'],
region_id=None,
publicurl=url_with_space,
internalurl=url_with_space,
adminurl=url_with_space,
url=url_with_space)
# add the endpoint to the database
PROVIDERS.catalog_api.create_endpoint(ref['id'], ref)
# delete the endpoint
self.delete('/endpoints/%s' % ref['id'])
# make sure it's deleted (GET should return Not Found)
self.get('/endpoints/%s' % ref['id'],
expected_status=http_client.NOT_FOUND)
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/$(project_id)s'
ref = unit.new_endpoint_ref(self.service_id,
interface='public',
region_id=self.region_id,
url=valid_url)
self.post('/endpoints', body={'endpoint': ref})
def test_endpoint_create_with_valid_url_project_id(self):
"""Create endpoint with valid url should be tested,too."""
valid_url = 'http://127.0.0.1:8774/v1.1/$(project_id)s'
ref = unit.new_endpoint_ref(self.service_id,
interface='public',
region_id=self.region_id,
url=valid_url)
self.post('/endpoints', body={'endpoint': ref})
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',
]
ref = unit.new_endpoint_ref(self.service_id)
for invalid_url in invalid_urls:
ref['url'] = invalid_url
self.post('/endpoints',
body={'endpoint': ref},
expected_status=http_client.BAD_REQUEST)
class TestMultiRegion(test_v3.RestfulTestCase):
def test_catalog_with_multi_region_reports_all_endpoints(self):
# Create two separate regions
first_region = self.post(
'/regions',
body={'region': unit.new_region_ref()}
).json_body['region']
second_region = self.post(
'/regions',
body={'region': unit.new_region_ref()}
).json_body['region']
# Create two services with the same type but separate name.
first_service = self.post(
'/services',
body={'service': unit.new_service_ref(type='foobar')}
).json_body['service']
second_service = self.post(
'/services',
body={'service': unit.new_service_ref(type='foobar')}
).json_body['service']
# Create an endpoint for each service
first_endpoint = self.post(
'/endpoints',
body={
'endpoint': unit.new_endpoint_ref(
first_service['id'],
interface='public',
region_id=first_region['id']
)
}
).json_body['endpoint']
second_endpoint = self.post(
'/endpoints',
body={
'endpoint': unit.new_endpoint_ref(
second_service['id'],
interface='public',
region_id=second_region['id']
)
}
).json_body['endpoint']
# Assert the endpoints and services from each region are in the
# catalog.
found_first_endpoint = False
found_second_endpoint = False
catalog = self.get('/auth/catalog/').json_body['catalog']
for service in catalog:
if service['id'] == first_service['id']:
endpoint = service['endpoints'][0]
self.assertEqual(endpoint['id'], first_endpoint['id'])
self.assertEqual(endpoint['region_id'], first_region['id'])
found_first_endpoint = True
elif service['id'] == second_service['id']:
endpoint = service['endpoints'][0]
self.assertEqual(endpoint['id'], second_endpoint['id'])
self.assertEqual(endpoint['region_id'], second_region['id'])
found_second_endpoint = True
self.assertTrue(found_first_endpoint)
self.assertTrue(found_second_endpoint)
class TestCatalogAPISQL(unit.TestCase):
"""Test for the catalog Manager against the SQL backend."""
def setUp(self):
super(TestCatalogAPISQL, self).setUp()
self.useFixture(database.Database())
self.load_backends()
service = unit.new_service_ref()
self.service_id = service['id']
PROVIDERS.catalog_api.create_service(self.service_id, service)
self.create_endpoint(service_id=self.service_id)
PROVIDERS.resource_api.create_domain(
default_fixtures.ROOT_DOMAIN['id'], default_fixtures.ROOT_DOMAIN)
def create_endpoint(self, service_id, **kwargs):
endpoint = unit.new_endpoint_ref(service_id=service_id,
region_id=None, **kwargs)
PROVIDERS.catalog_api.create_endpoint(endpoint['id'], endpoint)
return endpoint
def config_overrides(self):
super(TestCatalogAPISQL, 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
# create a project since the project should exist if we want to
# filter the catalog by the project or replace the url with a
# valid project id.
domain = unit.new_domain_ref()
PROVIDERS.resource_api.create_domain(domain['id'], domain)
project = unit.new_project_ref(domain_id=domain['id'])
PROVIDERS.resource_api.create_project(project['id'], project)
# the only endpoint in the catalog is the one created in setUp
catalog = PROVIDERS.catalog_api.get_v3_catalog(user_id, project['id'])
self.assertEqual(1, len(catalog[0]['endpoints']))
# it's also the only endpoint in the backend
self.assertEqual(1, len(PROVIDERS.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 = PROVIDERS.catalog_api.get_v3_catalog(user_id, project['id'])
self.assertEqual(1, len(catalog[0]['endpoints']))
# all three appear in the backend
self.assertEqual(3, len(PROVIDERS.catalog_api.list_endpoints()))
# create another valid endpoint - project_id will be replaced
self.create_endpoint(self.service_id,
url='http://keystone/%(project_id)s')
# there are two valid endpoints, positive check
catalog = PROVIDERS.catalog_api.get_v3_catalog(user_id, project['id'])
self.assertThat(catalog[0]['endpoints'], matchers.HasLength(2))
# If the URL has no 'project_id' to substitute, we will skip the
# endpoint which contains this kind of URL, negative check.
project_id = None
catalog = PROVIDERS.catalog_api.get_v3_catalog(user_id, project_id)
self.assertThat(catalog[0]['endpoints'], matchers.HasLength(1))
def test_get_catalog_always_returns_service_name(self):
user_id = uuid.uuid4().hex
# create a project since the project should exist if we want to
# filter the catalog by the project or replace the url with a
# valid project id.
domain = unit.new_domain_ref()
PROVIDERS.resource_api.create_domain(domain['id'], domain)
project = unit.new_project_ref(domain_id=domain['id'])
PROVIDERS.resource_api.create_project(project['id'], project)
# create a service, with a name
named_svc = unit.new_service_ref()
PROVIDERS.catalog_api.create_service(named_svc['id'], named_svc)
self.create_endpoint(service_id=named_svc['id'])
# create a service, with no name
unnamed_svc = unit.new_service_ref(name=None)
del unnamed_svc['name']
PROVIDERS.catalog_api.create_service(unnamed_svc['id'], unnamed_svc)
self.create_endpoint(service_id=unnamed_svc['id'])
catalog = PROVIDERS.catalog_api.get_v3_catalog(user_id, project['id'])
named_endpoint = [ep for ep in catalog
if ep['type'] == named_svc['type']][0]
self.assertEqual(named_svc['name'], named_endpoint['name'])
unnamed_endpoint = [ep for ep in catalog
if ep['type'] == unnamed_svc['type']][0]
self.assertEqual('', unnamed_endpoint['name'])
# TODO(dstanek): this needs refactoring with the test above, but we are in a
# crunch so that will happen in a future patch.
class TestCatalogAPISQLRegions(unit.TestCase):
"""Test for the catalog Manager against the SQL backend."""
def setUp(self):
super(TestCatalogAPISQLRegions, self).setUp()
self.useFixture(database.Database())
self.load_backends()
PROVIDERS.resource_api.create_domain(
default_fixtures.ROOT_DOMAIN['id'], default_fixtures.ROOT_DOMAIN)
def config_overrides(self):
super(TestCatalogAPISQLRegions, self).config_overrides()
self.config_fixture.config(group='catalog', driver='sql')
def test_get_catalog_returns_proper_endpoints_with_no_region(self):
service = unit.new_service_ref()
service_id = service['id']
PROVIDERS.catalog_api.create_service(service_id, service)
endpoint = unit.new_endpoint_ref(service_id=service_id,
region_id=None)
del endpoint['region_id']
PROVIDERS.catalog_api.create_endpoint(endpoint['id'], endpoint)
# create a project since the project should exist if we want to
# filter the catalog by the project or replace the url with a
# valid project id.
domain = unit.new_domain_ref()
PROVIDERS.resource_api.create_domain(domain['id'], domain)
project = unit.new_project_ref(domain_id=domain['id'])
PROVIDERS.resource_api.create_project(project['id'], project)
user_id = uuid.uuid4().hex
catalog = PROVIDERS.catalog_api.get_v3_catalog(user_id, project['id'])
self.assertValidCatalogEndpoint(
catalog[0]['endpoints'][0], ref=endpoint)
def test_get_catalog_returns_proper_endpoints_with_region(self):
service = unit.new_service_ref()
service_id = service['id']
PROVIDERS.catalog_api.create_service(service_id, service)
endpoint = unit.new_endpoint_ref(service_id=service_id)
region = unit.new_region_ref(id=endpoint['region_id'])
PROVIDERS.catalog_api.create_region(region)
PROVIDERS.catalog_api.create_endpoint(endpoint['id'], endpoint)
endpoint = PROVIDERS.catalog_api.get_endpoint(endpoint['id'])
user_id = uuid.uuid4().hex
# create a project since the project should exist if we want to
# filter the catalog by the project or replace the url with a
# valid project id.
domain = unit.new_domain_ref()
PROVIDERS.resource_api.create_domain(domain['id'], domain)
project = unit.new_project_ref(domain_id=domain['id'])
PROVIDERS.resource_api.create_project(project['id'], project)
catalog = PROVIDERS.catalog_api.get_v3_catalog(user_id, project['id'])
self.assertValidCatalogEndpoint(
catalog[0]['endpoints'][0], ref=endpoint)
def assertValidCatalogEndpoint(self, entity, ref=None):
keys = ['description', 'id', 'interface', 'name', 'region_id', 'url']
for k in keys:
self.assertEqual(ref.get(k), entity[k], k)
self.assertEqual(entity['region_id'], entity['region'])
class TestCatalogAPITemplatedProject(test_v3.RestfulTestCase):
"""Templated Catalog doesn't support full API.
Eg. No region/endpoint creation.
"""
def config_overrides(self):
super(TestCatalogAPITemplatedProject, self).config_overrides()
self.config_fixture.config(group='catalog', driver='templated')
def load_fixtures(self, fixtures):
self.load_sample_data(create_region_and_endpoints=False)
def test_project_delete(self):
"""Deleting a project should not result in an 500 ISE.
Deleting a project will create a notification, which the EndpointFilter
functionality will use to clean up any project->endpoint and
project->endpoint_group relationships. The templated catalog does not
support such relationships, but the act of attempting to delete them
should not cause a NotImplemented exception to be exposed to an API
caller.
Deleting an endpoint has a similar notification and clean up
mechanism, but since we do not allow deletion of endpoints with the
templated catalog, there is no testing to do for that action.
"""
self.delete(
'/projects/%(project_id)s' % {
'project_id': self.project_id})