Fix zone transfer requests + add tempest tests

Change-Id: I904fa976b345135bbfa19aca8f39c97d063973fa
Fixes-Bug: #1482270
This commit is contained in:
Graham Hayes 2015-08-06 16:52:38 +01:00
parent d5c63b6ddb
commit cad363aecd
9 changed files with 417 additions and 11 deletions

View File

@ -13,11 +13,11 @@
# 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 six
import pecan
from oslo_log import log as logging
from designate import utils
from designate import exceptions
from designate.api.v2.controllers import rest
from designate.objects import ZoneTransferRequest
from designate.objects.adapters import DesignateAdapter
@ -72,12 +72,8 @@ class TransferRequestsController(rest.RestController):
context = request.environ['context']
try:
body = request.body_dict
except Exception as e:
if six.text_type(e) != 'TODO: Unsupported Content Type':
raise
else:
# Got a blank body
body = dict()
except exceptions.EmptyRequestBody:
body = dict()
body['zone_id'] = zone_id

View File

@ -18,12 +18,13 @@ from inspect import getargspec
import six
from oslo_serialization import jsonutils
from oslo_log import log as logging
import pecan.core
from designate import exceptions
JSON_TYPES = ('application/json', 'application/json-patch+json')
LOG = logging.getLogger(__name__)
class Request(pecan.core.Request):
@ -34,13 +35,16 @@ class Request(pecan.core.Request):
Content-Type header.
We add this method to ease future XML support, so the main code
is not hardcoded to call pecans "request.json()" method.
is not hardcoded to call pecans "request.json" method.
"""
if self.content_type in JSON_TYPES:
try:
return jsonutils.load(self.body_file)
except ValueError as valueError:
raise exceptions.InvalidJson(six.text_type(valueError))
if len(self.body) == 0:
raise exceptions.EmptyRequestBody('Request Body is empty')
else:
raise exceptions.InvalidJson(six.text_type(valueError))
else:
raise exceptions.UnsupportedContentType(
'Content-type must be application/json')

View File

@ -110,6 +110,11 @@ class BadRequest(Base):
expected = True
class EmptyRequestBody(BadRequest):
error_type = 'empty_request_body'
expected = True
class InvalidUUID(BadRequest):
error_type = 'invalid_uuid'

View File

@ -0,0 +1,66 @@
"""
Copyright 2015 Rackspace
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.
"""
from functionaltests.api.v2.models.transfer_accepts_model import \
TransferAcceptsModel
from functionaltests.api.v2.models.transfer_accepts_model import \
TransferAcceptsListModel
from functionaltests.common.client import ClientMixin
class TransferAcceptClient(ClientMixin):
@classmethod
def transfer_accepts_uri(cls, filters=None):
url = "/v2/zones/tasks/transfer_accepts"
if filters:
url = cls.add_filters(url, filters)
return url
@classmethod
def transfer_accept_uri(cls, transfer_request_id):
return "/v2/zones/tasks/transfer_accepts/{1}".format(
transfer_request_id)
def list_transfer_accepts(self, zone_id, filters=None, **kwargs):
resp, body = self.client.get(
self.transfer_accepts_uri(filters), **kwargs)
return self.deserialize(resp, body, TransferAcceptsListModel)
def get_transfer_accept(self, zone_id, transfer_request_id, **kwargs):
resp, body = self.client.get(self.transfer_accept_uri(
transfer_request_id),
**kwargs)
return self.deserialize(resp, body, TransferAcceptsModel)
def post_transfer_accept(self, transfer_request_model, **kwargs):
resp, body = self.client.post(
self.transfer_accepts_uri(),
body=transfer_request_model.to_json(),
**kwargs)
return self.deserialize(resp, body, TransferAcceptsModel)
def put_transfer_accept(self, zone_id, transfer_request_id,
transfer_request_model, **kwargs):
resp, body = self.client.put(self.transfer_accept_uri(
transfer_request_id),
body=transfer_request_model.to_json(), **kwargs)
return self.deserialize(resp, body, TransferAcceptsModel)
def delete_transfer_accept(self, zone_id, transfer_request_id, **kwargs):
resp, body = self.client.delete(
self.transfer_accept_uri(zone_id, transfer_request_id), **kwargs)
return self.deserialize(resp, body, TransferAcceptsModel)

View File

@ -0,0 +1,81 @@
"""
Copyright 2015 Rackspace
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.
"""
from functionaltests.api.v2.models.transfer_requests_model import \
TransferRequestsModel
from functionaltests.api.v2.models.transfer_requests_model import \
TransferRequestsListModel
from functionaltests.common.client import ClientMixin
class TransferRequestClient(ClientMixin):
@classmethod
def create_transfer_requests_uri(cls, zone_id, filters=None):
url = "/v2/zones/{0}/tasks/transfer_requests".format(zone_id)
if filters:
url = cls.add_filters(url, filters)
return url
@classmethod
def transfer_requests_uri(cls, filters=None):
url = "/v2/zones/tasks/transfer_requests"
if filters:
url = cls.add_filters(url, filters)
return url
@classmethod
def transfer_request_uri(cls, transfer_request_id):
return "/v2/zones/tasks/transfer_requests/{0}".format(
transfer_request_id)
def list_transfer_requests(self, filters=None, **kwargs):
resp, body = self.client.get(
self.transfer_requests_uri(filters), **kwargs)
return self.deserialize(resp, body, TransferRequestsListModel)
def get_transfer_request(self, transfer_request_id, **kwargs):
resp, body = self.client.get(self.transfer_request_uri(
transfer_request_id),
**kwargs)
return self.deserialize(resp, body, TransferRequestsModel)
def post_transfer_request(self, zone_id, transfer_request_model=None,
**kwargs):
resp, body = self.client.post(
self.create_transfer_requests_uri(zone_id),
body=transfer_request_model.to_json(),
**kwargs)
return self.deserialize(resp, body, TransferRequestsModel)
def post_transfer_request_empty_body(self, zone_id, **kwargs):
resp, body = self.client.post(
self.create_transfer_requests_uri(zone_id),
body=None,
**kwargs)
return self.deserialize(resp, body, TransferRequestsModel)
def put_transfer_request(self, transfer_request_id,
transfer_request_model, **kwargs):
resp, body = self.client.put(self.transfer_request_uri(
transfer_request_id),
body=transfer_request_model.to_json(), **kwargs)
return self.deserialize(resp, body, TransferRequestsModel)
def delete_transfer_request(self, transfer_request_id, **kwargs):
resp, body = self.client.delete(
self.transfer_request_uri(transfer_request_id), **kwargs)
return self.deserialize(resp, body, TransferRequestsModel)

View File

@ -0,0 +1,33 @@
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Author: Endre Karlson <endre.karlson@hp.com>
#
# 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.
from functionaltests.common.models import BaseModel
from functionaltests.common.models import CollectionModel
from functionaltests.common.models import EntityModel
class TransferAcceptsData(BaseModel):
pass
class TransferAcceptsModel(EntityModel):
ENTITY_NAME = 'transfer_accept'
MODEL_TYPE = TransferAcceptsData
class TransferAcceptsListModel(CollectionModel):
COLLECTION_NAME = 'transfer_accepts'
MODEL_TYPE = TransferAcceptsData

View File

@ -0,0 +1,33 @@
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Author: Endre Karlson <endre.karlson@hp.com>
#
# 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.
from functionaltests.common.models import BaseModel
from functionaltests.common.models import CollectionModel
from functionaltests.common.models import EntityModel
class TransferRequestsData(BaseModel):
pass
class TransferRequestsModel(EntityModel):
ENTITY_NAME = 'transfer_request'
MODEL_TYPE = TransferRequestsData
class TransferRequestsListModel(CollectionModel):
COLLECTION_NAME = 'transfer_requests'
MODEL_TYPE = TransferRequestsData

View File

@ -0,0 +1,149 @@
"""
Copyright 2015 Rackspace
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.
"""
from tempest_lib import exceptions
from functionaltests.common import datagen
from functionaltests.common import utils
from functionaltests.api.v2.base import DesignateV2Test
from functionaltests.api.v2.clients.transfer_requests_client import \
TransferRequestClient
from functionaltests.api.v2.clients.transfer_accepts_client import \
TransferAcceptClient
from functionaltests.api.v2.clients.zone_client import ZoneClient
@utils.parameterized_class
class TransferZoneOwnerShipTest(DesignateV2Test):
def setUp(self):
super(TransferZoneOwnerShipTest, self).setUp()
self.increase_quotas(user='default')
self.increase_quotas(user='alt')
resp, self.zone = ZoneClient.as_user('default').post_zone(
datagen.random_zone_data())
ZoneClient.as_user('default').wait_for_zone(self.zone.id)
def test_list_transfer_requests(self):
resp, model = TransferRequestClient.as_user('default') \
.list_transfer_requests()
self.assertEqual(resp.status, 200)
def test_create_zone_transfer_request(self):
resp, transfer_request = TransferRequestClient.as_user('default')\
.post_transfer_request(self.zone.id,
datagen.random_transfer_request_data())
self.assertEqual(resp.status, 201)
def test_view_zone_transfer_request(self):
resp, transfer_request = TransferRequestClient.as_user('default')\
.post_transfer_request(self.zone.id,
datagen.random_transfer_request_data())
self.assertEqual(resp.status, 201)
resp, transfer_request = TransferRequestClient.as_user('alt')\
.get_transfer_request(transfer_request.id)
self.assertEqual(resp.status, 200)
self.assertEqual(getattr(transfer_request, 'key', None), None)
def test_create_zone_transfer_request_scoped(self):
target_project_id = TransferRequestClient.as_user('alt').tenant_id
resp, transfer_request = TransferRequestClient.as_user('default')\
.post_transfer_request(self.zone.id,
datagen.random_transfer_request_data(
target_project_id=target_project_id))
self.assertEqual(resp.status, 201)
self.assertEqual(transfer_request.target_project_id, target_project_id)
resp, transfer_request = TransferRequestClient.as_user('alt')\
.get_transfer_request(transfer_request.id)
self.assertEqual(resp.status, 200)
def test_view_zone_transfer_request_scoped(self):
target_project_id = TransferRequestClient.as_user('admin').tenant_id
resp, transfer_request = TransferRequestClient.as_user('default')\
.post_transfer_request(self.zone.id,
datagen.random_transfer_request_data(
target_project_id=target_project_id))
self.assertEqual(resp.status, 201)
self.assertEqual(transfer_request.target_project_id, target_project_id)
self._assert_exception(
exceptions.NotFound, 'zone_transfer_request_not_found', 404,
TransferRequestClient.as_user('alt').get_transfer_request,
self.zone.id)
resp, transfer_request = TransferRequestClient.as_user('admin')\
.get_transfer_request(transfer_request.id)
self.assertEqual(resp.status, 200)
def test_create_zone_transfer_request_no_body(self):
resp, transfer_request = TransferRequestClient.as_user('default')\
.post_transfer_request_empty_body(self.zone.id)
self.assertEqual(resp.status, 201)
def test_do_zone_transfer(self):
resp, transfer_request = TransferRequestClient.as_user('default')\
.post_transfer_request(self.zone.id,
datagen.random_transfer_request_data())
self.assertEqual(resp.status, 201)
resp, transfer_accept = TransferAcceptClient.as_user('alt')\
.post_transfer_accept(
datagen.random_transfer_accept_data(
key=transfer_request.key,
zone_transfer_request_id=transfer_request.id
))
self.assertEqual(resp.status, 201)
def test_do_zone_transfer_scoped(self):
target_project_id = TransferRequestClient.as_user('alt').tenant_id
resp, transfer_request = TransferRequestClient.as_user('default')\
.post_transfer_request(self.zone.id,
datagen.random_transfer_request_data(
target_project_id=target_project_id))
self.assertEqual(resp.status, 201)
resp, retrived_transfer_request = TransferRequestClient.\
as_user('alt').get_transfer_request(transfer_request.id)
self.assertEqual(resp.status, 200)
resp, transfer_accept = TransferAcceptClient.as_user('alt')\
.post_transfer_accept(
datagen.random_transfer_accept_data(
key=transfer_request.key,
zone_transfer_request_id=transfer_request.id
))
self.assertEqual(resp.status, 201)
client = ZoneClient.as_user('default')
self._assert_exception(
exceptions.NotFound, 'domain_not_found', 404,
client.get_zone, self.zone.id)
resp, zone = ZoneClient.as_user('alt').get_zone(self.zone.id)
self.assertEqual(resp.status, 200)

View File

@ -13,11 +13,15 @@ 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
import random
from functionaltests.api.v2.models.blacklist_model import BlacklistModel
from functionaltests.api.v2.models.pool_model import PoolModel
from functionaltests.api.v2.models.transfer_requests_model import \
TransferRequestsModel
from functionaltests.api.v2.models.transfer_accepts_model import \
TransferAcceptsModel
from functionaltests.api.v2.models.recordset_model import RecordsetModel
from functionaltests.api.v2.models.zone_model import ZoneModel
@ -33,6 +37,10 @@ def random_ipv6():
return result.replace("0000", "0")
def random_uuid():
return uuid.uuid4()
def random_string(prefix='rand', n=8, suffix=''):
"""Return a string containing random digits
@ -64,6 +72,37 @@ def random_zone_data(name=None, email=None, ttl=None, description=None):
'description': description})
def random_transfer_request_data(description=None, target_project_id=None):
"""Generate random zone data, with optional overrides
:return: A TransferRequestModel
"""
data = {}
if description is None:
data['description'] = random_string(prefix='Description ')
if target_project_id:
data['target_project_id'] = target_project_id
return TransferRequestsModel.from_dict(data)
def random_transfer_accept_data(key=None, zone_transfer_request_id=None):
"""Generate random zone data, with optional overrides
:return: A TransferRequestModel
"""
if key is None:
key = random_string()
if zone_transfer_request_id is None:
zone_transfer_request_id = random_uuid()
return TransferAcceptsModel.from_dict({
'key': key,
'zone_transfer_request_id': zone_transfer_request_id})
def random_recordset_data(record_type, zone_name, name=None, records=None,
ttl=None):
"""Generate random recordset data, with optional overrides