Merge "Get mandatory patch attrs from WSME properties"
This commit is contained in:
commit
0d661c32f0
|
@ -40,22 +40,6 @@ from magnum.objects import fields
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class BayPatchType(types.JsonPatchType):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def mandatory_attrs():
|
|
||||||
return ['/baymodel_id']
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def internal_attrs():
|
|
||||||
internal_attrs = ['/api_address', '/node_addresses',
|
|
||||||
'/master_addresses', '/stack_id',
|
|
||||||
'/ca_cert_ref', '/magnum_cert_ref',
|
|
||||||
'/trust_id', '/trustee_user_name',
|
|
||||||
'/trustee_password', '/trustee_user_id']
|
|
||||||
return types.JsonPatchType.internal_attrs() + internal_attrs
|
|
||||||
|
|
||||||
|
|
||||||
class BayID(wtypes.Base):
|
class BayID(wtypes.Base):
|
||||||
uuid = types.uuid
|
uuid = types.uuid
|
||||||
|
|
||||||
|
@ -185,6 +169,19 @@ class Bay(base.APIBase):
|
||||||
return cls._convert_with_links(sample, 'http://localhost:9511', expand)
|
return cls._convert_with_links(sample, 'http://localhost:9511', expand)
|
||||||
|
|
||||||
|
|
||||||
|
class BayPatchType(types.JsonPatchType):
|
||||||
|
_api_base = Bay
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def internal_attrs():
|
||||||
|
internal_attrs = ['/api_address', '/node_addresses',
|
||||||
|
'/master_addresses', '/stack_id',
|
||||||
|
'/ca_cert_ref', '/magnum_cert_ref',
|
||||||
|
'/trust_id', '/trustee_user_name',
|
||||||
|
'/trustee_password', '/trustee_user_id']
|
||||||
|
return types.JsonPatchType.internal_attrs() + internal_attrs
|
||||||
|
|
||||||
|
|
||||||
class BayCollection(collection.Collection):
|
class BayCollection(collection.Collection):
|
||||||
"""API representation of a collection of bays."""
|
"""API representation of a collection of bays."""
|
||||||
|
|
||||||
|
|
|
@ -33,15 +33,6 @@ from magnum import objects
|
||||||
from magnum.objects import fields
|
from magnum.objects import fields
|
||||||
|
|
||||||
|
|
||||||
class BayModelPatchType(types.JsonPatchType):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def mandatory_attrs():
|
|
||||||
return ['/image_id', '/keypair_id', '/external_network_id', '/coe',
|
|
||||||
'/tls_disabled', '/public', '/registry_enabled',
|
|
||||||
'/server_type', '/cluster_distro', '/network_driver']
|
|
||||||
|
|
||||||
|
|
||||||
class BayModel(base.APIBase):
|
class BayModel(base.APIBase):
|
||||||
"""API representation of a baymodel.
|
"""API representation of a baymodel.
|
||||||
|
|
||||||
|
@ -206,6 +197,14 @@ class BayModel(base.APIBase):
|
||||||
return cls._convert_with_links(sample, 'http://localhost:9511')
|
return cls._convert_with_links(sample, 'http://localhost:9511')
|
||||||
|
|
||||||
|
|
||||||
|
class BayModelPatchType(types.JsonPatchType):
|
||||||
|
_api_base = BayModel
|
||||||
|
_extra_non_removable_attrs = {'/network_driver', '/external_network_id',
|
||||||
|
'/tls_disabled', '/public', '/server_type',
|
||||||
|
'/coe', '/registry_enabled',
|
||||||
|
'/cluster_distro'}
|
||||||
|
|
||||||
|
|
||||||
class BayModelCollection(collection.Collection):
|
class BayModelCollection(collection.Collection):
|
||||||
"""API representation of a collection of baymodels."""
|
"""API representation of a collection of baymodels."""
|
||||||
|
|
||||||
|
|
|
@ -40,21 +40,6 @@ from magnum.objects import fields
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ClusterPatchType(types.JsonPatchType):
|
|
||||||
@staticmethod
|
|
||||||
def mandatory_attrs():
|
|
||||||
return ['/cluster_template_id']
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def internal_attrs():
|
|
||||||
internal_attrs = ['/api_address', '/node_addresses',
|
|
||||||
'/master_addresses', '/stack_id',
|
|
||||||
'/ca_cert_ref', '/magnum_cert_ref',
|
|
||||||
'/trust_id', '/trustee_user_name',
|
|
||||||
'/trustee_password', '/trustee_user_id']
|
|
||||||
return types.JsonPatchType.internal_attrs() + internal_attrs
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterID(wtypes.Base):
|
class ClusterID(wtypes.Base):
|
||||||
"""API representation of a cluster ID
|
"""API representation of a cluster ID
|
||||||
|
|
||||||
|
@ -237,6 +222,19 @@ class Cluster(base.APIBase):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterPatchType(types.JsonPatchType):
|
||||||
|
_api_base = Cluster
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def internal_attrs():
|
||||||
|
internal_attrs = ['/api_address', '/node_addresses',
|
||||||
|
'/master_addresses', '/stack_id',
|
||||||
|
'/ca_cert_ref', '/magnum_cert_ref',
|
||||||
|
'/trust_id', '/trustee_user_name',
|
||||||
|
'/trustee_password', '/trustee_user_id']
|
||||||
|
return types.JsonPatchType.internal_attrs() + internal_attrs
|
||||||
|
|
||||||
|
|
||||||
class ClusterCollection(collection.Collection):
|
class ClusterCollection(collection.Collection):
|
||||||
"""API representation of a collection of clusters."""
|
"""API representation of a collection of clusters."""
|
||||||
|
|
||||||
|
|
|
@ -33,15 +33,6 @@ from magnum import objects
|
||||||
from magnum.objects import fields
|
from magnum.objects import fields
|
||||||
|
|
||||||
|
|
||||||
class ClusterTemplatePatchType(types.JsonPatchType):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def mandatory_attrs():
|
|
||||||
return ['/image_id', '/keypair_id', '/external_network_id', '/coe',
|
|
||||||
'/tls_disabled', '/public', '/registry_enabled',
|
|
||||||
'/server_type', '/cluster_distro', '/network_driver']
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterTemplate(base.APIBase):
|
class ClusterTemplate(base.APIBase):
|
||||||
"""API representation of a clustertemplate.
|
"""API representation of a clustertemplate.
|
||||||
|
|
||||||
|
@ -209,6 +200,14 @@ class ClusterTemplate(base.APIBase):
|
||||||
return cls._convert_with_links(sample, 'http://localhost:9511')
|
return cls._convert_with_links(sample, 'http://localhost:9511')
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterTemplatePatchType(types.JsonPatchType):
|
||||||
|
_api_base = ClusterTemplate
|
||||||
|
_extra_non_removable_attrs = {'/network_driver', '/external_network_id',
|
||||||
|
'/tls_disabled', '/public', '/server_type',
|
||||||
|
'/coe', '/registry_enabled',
|
||||||
|
'/cluster_distro'}
|
||||||
|
|
||||||
|
|
||||||
class ClusterTemplateCollection(collection.Collection):
|
class ClusterTemplateCollection(collection.Collection):
|
||||||
"""API representation of a collection of clustertemplates."""
|
"""API representation of a collection of clustertemplates."""
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
import wsme
|
import wsme
|
||||||
|
@ -143,6 +145,17 @@ class JsonPatchType(wtypes.Base):
|
||||||
mandatory=True)
|
mandatory=True)
|
||||||
value = MultiType(wtypes.text, int)
|
value = MultiType(wtypes.text, int)
|
||||||
|
|
||||||
|
# The class of the objects being patched. Override this in subclasses.
|
||||||
|
# Should probably be a subclass of magnum.api.controllers.base.APIBase.
|
||||||
|
_api_base = None
|
||||||
|
|
||||||
|
# Attributes that are not required for construction, but which may not be
|
||||||
|
# removed if set. Override in subclasses if needed.
|
||||||
|
_extra_non_removable_attrs = set()
|
||||||
|
|
||||||
|
# Set of non-removable attributes, calculated lazily.
|
||||||
|
_non_removable_attrs = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def internal_attrs():
|
def internal_attrs():
|
||||||
"""Returns a list of internal attributes.
|
"""Returns a list of internal attributes.
|
||||||
|
@ -154,15 +167,24 @@ class JsonPatchType(wtypes.Base):
|
||||||
return ['/created_at', '/id', '/links', '/updated_at',
|
return ['/created_at', '/id', '/links', '/updated_at',
|
||||||
'/uuid', '/project_id', '/user_id']
|
'/uuid', '/project_id', '/user_id']
|
||||||
|
|
||||||
@staticmethod
|
@classmethod
|
||||||
def mandatory_attrs():
|
def non_removable_attrs(self):
|
||||||
"""Retruns a list of mandatory attributes.
|
"""Returns a set of names of attributes that may not be removed.
|
||||||
|
|
||||||
Mandatory attributes can't be removed from the document. This
|
|
||||||
method should be overwritten by derived class.
|
|
||||||
|
|
||||||
|
Attributes whose 'mandatory' property is True are automatically added
|
||||||
|
to this set. To add additional attributes to the set, override the
|
||||||
|
field _extra_non_removable_attrs in subclasses, with a set of the form
|
||||||
|
{'/foo', '/bar'}.
|
||||||
"""
|
"""
|
||||||
return []
|
if self._non_removable_attrs is None:
|
||||||
|
self._non_removable_attrs = self._extra_non_removable_attrs.copy()
|
||||||
|
if self._api_base:
|
||||||
|
fields = inspect.getmembers(self._api_base,
|
||||||
|
lambda a: not inspect.isroutine(a))
|
||||||
|
for name, field in fields:
|
||||||
|
if getattr(field, 'mandatory', False):
|
||||||
|
self._non_removable_attrs.add('/%s' % name)
|
||||||
|
return self._non_removable_attrs
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate(patch):
|
def validate(patch):
|
||||||
|
@ -170,7 +192,7 @@ class JsonPatchType(wtypes.Base):
|
||||||
msg = _("'%s' is an internal attribute and can not be updated")
|
msg = _("'%s' is an internal attribute and can not be updated")
|
||||||
raise wsme.exc.ClientSideError(msg % patch.path)
|
raise wsme.exc.ClientSideError(msg % patch.path)
|
||||||
|
|
||||||
if patch.path in patch.mandatory_attrs() and patch.op == 'remove':
|
if patch.path in patch.non_removable_attrs() and patch.op == 'remove':
|
||||||
msg = _("'%s' is a mandatory attribute and can not be removed")
|
msg = _("'%s' is a mandatory attribute and can not be removed")
|
||||||
raise wsme.exc.ClientSideError(msg % patch.path)
|
raise wsme.exc.ClientSideError(msg % patch.path)
|
||||||
|
|
||||||
|
|
|
@ -384,21 +384,15 @@ class TestPatch(api_base.FunctionalTest):
|
||||||
self.assertEqual(self.bay.name, response['name'])
|
self.assertEqual(self.bay.name, response['name'])
|
||||||
self.assertEqual(self.bay.master_count, response['master_count'])
|
self.assertEqual(self.bay.master_count, response['master_count'])
|
||||||
|
|
||||||
def test_remove_uuid(self):
|
def test_remove_mandatory_property_fail(self):
|
||||||
response = self.patch_json('/bays/%s' % self.bay.uuid,
|
mandatory_properties = ('/uuid', '/baymodel_id')
|
||||||
[{'path': '/uuid', 'op': 'remove'}],
|
for p in mandatory_properties:
|
||||||
expect_errors=True)
|
response = self.patch_json('/bays/%s' % self.bay.uuid,
|
||||||
self.assertEqual(400, response.status_int)
|
[{'path': p, 'op': 'remove'}],
|
||||||
self.assertEqual('application/json', response.content_type)
|
expect_errors=True)
|
||||||
self.assertTrue(response.json['errors'])
|
self.assertEqual(400, response.status_int)
|
||||||
|
self.assertEqual('application/json', response.content_type)
|
||||||
def test_remove_baymodel_id(self):
|
self.assertTrue(response.json['errors'])
|
||||||
response = self.patch_json('/bays/%s' % self.bay.uuid,
|
|
||||||
[{'path': '/baymodel_id', 'op': 'remove'}],
|
|
||||||
expect_errors=True)
|
|
||||||
self.assertEqual(400, response.status_int)
|
|
||||||
self.assertEqual('application/json', response.content_type)
|
|
||||||
self.assertTrue(response.json['errors'])
|
|
||||||
|
|
||||||
def test_remove_non_existent_property(self):
|
def test_remove_non_existent_property(self):
|
||||||
response = self.patch_json(
|
response = self.patch_json(
|
||||||
|
|
|
@ -368,39 +368,37 @@ class TestPatch(api_base.FunctionalTest):
|
||||||
self.assertTrue(response.json['errors'])
|
self.assertTrue(response.json['errors'])
|
||||||
|
|
||||||
def test_remove_singular(self):
|
def test_remove_singular(self):
|
||||||
baymodel = obj_utils.create_test_baymodel(
|
response = self.get_json('/baymodels/%s' % self.baymodel.uuid)
|
||||||
self.context,
|
|
||||||
uuid=uuidutils.generate_uuid())
|
|
||||||
response = self.get_json('/baymodels/%s' % baymodel.uuid)
|
|
||||||
self.assertIsNotNone(response['dns_nameserver'])
|
self.assertIsNotNone(response['dns_nameserver'])
|
||||||
|
|
||||||
response = self.patch_json('/baymodels/%s' % baymodel.uuid,
|
response = self.patch_json('/baymodels/%s' % self.baymodel.uuid,
|
||||||
[{'path': '/dns_nameserver',
|
[{'path': '/dns_nameserver',
|
||||||
'op': 'remove'}])
|
'op': 'remove'}])
|
||||||
self.assertEqual('application/json', response.content_type)
|
self.assertEqual('application/json', response.content_type)
|
||||||
self.assertEqual(200, response.status_code)
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
response = self.get_json('/baymodels/%s' % baymodel.uuid)
|
response = self.get_json('/baymodels/%s' % self.baymodel.uuid)
|
||||||
self.assertIsNone(response['dns_nameserver'])
|
self.assertIsNone(response['dns_nameserver'])
|
||||||
# Assert nothing else was changed
|
# Assert nothing else was changed
|
||||||
self.assertEqual(baymodel.uuid, response['uuid'])
|
self.assertEqual(self.baymodel.uuid, response['uuid'])
|
||||||
self.assertEqual(baymodel.name, response['name'])
|
self.assertEqual(self.baymodel.name, response['name'])
|
||||||
self.assertEqual(baymodel.apiserver_port, response['apiserver_port'])
|
self.assertEqual(self.baymodel.apiserver_port,
|
||||||
self.assertEqual(baymodel.image_id,
|
response['apiserver_port'])
|
||||||
|
self.assertEqual(self.baymodel.image_id,
|
||||||
response['image_id'])
|
response['image_id'])
|
||||||
self.assertEqual(baymodel.fixed_network,
|
self.assertEqual(self.baymodel.fixed_network,
|
||||||
response['fixed_network'])
|
response['fixed_network'])
|
||||||
self.assertEqual(baymodel.network_driver,
|
self.assertEqual(self.baymodel.network_driver,
|
||||||
response['network_driver'])
|
response['network_driver'])
|
||||||
self.assertEqual(baymodel.volume_driver,
|
self.assertEqual(self.baymodel.volume_driver,
|
||||||
response['volume_driver'])
|
response['volume_driver'])
|
||||||
self.assertEqual(baymodel.docker_volume_size,
|
self.assertEqual(self.baymodel.docker_volume_size,
|
||||||
response['docker_volume_size'])
|
response['docker_volume_size'])
|
||||||
self.assertEqual(baymodel.coe, response['coe'])
|
self.assertEqual(self.baymodel.coe, response['coe'])
|
||||||
self.assertEqual(baymodel.http_proxy, response['http_proxy'])
|
self.assertEqual(self.baymodel.http_proxy, response['http_proxy'])
|
||||||
self.assertEqual(baymodel.https_proxy, response['https_proxy'])
|
self.assertEqual(self.baymodel.https_proxy, response['https_proxy'])
|
||||||
self.assertEqual(baymodel.no_proxy, response['no_proxy'])
|
self.assertEqual(self.baymodel.no_proxy, response['no_proxy'])
|
||||||
self.assertEqual(baymodel.labels, response['labels'])
|
self.assertEqual(self.baymodel.labels, response['labels'])
|
||||||
|
|
||||||
def test_remove_non_existent_property_fail(self):
|
def test_remove_non_existent_property_fail(self):
|
||||||
response = self.patch_json('/baymodels/%s' % self.baymodel.uuid,
|
response = self.patch_json('/baymodels/%s' % self.baymodel.uuid,
|
||||||
|
@ -411,10 +409,10 @@ class TestPatch(api_base.FunctionalTest):
|
||||||
self.assertTrue(response.json['errors'])
|
self.assertTrue(response.json['errors'])
|
||||||
|
|
||||||
def test_remove_mandatory_property_fail(self):
|
def test_remove_mandatory_property_fail(self):
|
||||||
mandatory_properties = ('/image_id', '/keypair_id',
|
mandatory_properties = ('/image_id', '/keypair_id', '/coe',
|
||||||
'/external_network_id', '/coe',
|
'/external_network_id', '/server_type',
|
||||||
'/tls_disabled', '/public',
|
'/tls_disabled', '/public',
|
||||||
'/registry_enabled', '/server_type',
|
'/registry_enabled',
|
||||||
'/cluster_distro', '/network_driver')
|
'/cluster_distro', '/network_driver')
|
||||||
for p in mandatory_properties:
|
for p in mandatory_properties:
|
||||||
response = self.patch_json('/baymodels/%s' % self.baymodel.uuid,
|
response = self.patch_json('/baymodels/%s' % self.baymodel.uuid,
|
||||||
|
|
|
@ -391,22 +391,15 @@ class TestPatch(api_base.FunctionalTest):
|
||||||
self.assertEqual(self.cluster_obj.master_count,
|
self.assertEqual(self.cluster_obj.master_count,
|
||||||
response['master_count'])
|
response['master_count'])
|
||||||
|
|
||||||
def test_remove_uuid(self):
|
def test_remove_mandatory_property_fail(self):
|
||||||
response = self.patch_json('/clusters/%s' % self.cluster_obj.uuid,
|
mandatory_properties = ('/uuid', '/cluster_template_id')
|
||||||
[{'path': '/uuid', 'op': 'remove'}],
|
for p in mandatory_properties:
|
||||||
expect_errors=True)
|
response = self.patch_json('/clusters/%s' % self.cluster_obj.uuid,
|
||||||
self.assertEqual(400, response.status_int)
|
[{'path': p, 'op': 'remove'}],
|
||||||
self.assertEqual('application/json', response.content_type)
|
expect_errors=True)
|
||||||
self.assertTrue(response.json['errors'])
|
self.assertEqual(400, response.status_int)
|
||||||
|
self.assertEqual('application/json', response.content_type)
|
||||||
def test_remove_cluster_template_id(self):
|
self.assertTrue(response.json['errors'])
|
||||||
response = self.patch_json('/clusters/%s' % self.cluster_obj.uuid,
|
|
||||||
[{'path': '/cluster_template_id',
|
|
||||||
'op': 'remove'}],
|
|
||||||
expect_errors=True)
|
|
||||||
self.assertEqual(400, response.status_int)
|
|
||||||
self.assertEqual('application/json', response.content_type)
|
|
||||||
self.assertTrue(response.json['errors'])
|
|
||||||
|
|
||||||
def test_remove_non_existent_property(self):
|
def test_remove_non_existent_property(self):
|
||||||
response = self.patch_json(
|
response = self.patch_json(
|
||||||
|
|
|
@ -394,43 +394,42 @@ class TestPatch(api_base.FunctionalTest):
|
||||||
self.assertTrue(response.json['errors'])
|
self.assertTrue(response.json['errors'])
|
||||||
|
|
||||||
def test_remove_singular(self):
|
def test_remove_singular(self):
|
||||||
cluster_template = obj_utils.create_test_cluster_template(
|
|
||||||
self.context,
|
|
||||||
uuid=uuidutils.generate_uuid())
|
|
||||||
response = self.get_json('/clustertemplates/%s' %
|
response = self.get_json('/clustertemplates/%s' %
|
||||||
cluster_template.uuid)
|
self.cluster_template.uuid)
|
||||||
self.assertIsNotNone(response['dns_nameserver'])
|
self.assertIsNotNone(response['dns_nameserver'])
|
||||||
|
|
||||||
response = self.patch_json('/clustertemplates/%s' %
|
response = self.patch_json('/clustertemplates/%s' %
|
||||||
cluster_template.uuid,
|
self.cluster_template.uuid,
|
||||||
[{'path': '/dns_nameserver',
|
[{'path': '/dns_nameserver',
|
||||||
'op': 'remove'}])
|
'op': 'remove'}])
|
||||||
self.assertEqual('application/json', response.content_type)
|
self.assertEqual('application/json', response.content_type)
|
||||||
self.assertEqual(200, response.status_code)
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
response = self.get_json('/clustertemplates/%s' %
|
response = self.get_json('/clustertemplates/%s' %
|
||||||
cluster_template.uuid)
|
self.cluster_template.uuid)
|
||||||
self.assertIsNone(response['dns_nameserver'])
|
self.assertIsNone(response['dns_nameserver'])
|
||||||
# Assert nothing else was changed
|
# Assert nothing else was changed
|
||||||
self.assertEqual(cluster_template.uuid, response['uuid'])
|
self.assertEqual(self.cluster_template.uuid, response['uuid'])
|
||||||
self.assertEqual(cluster_template.name, response['name'])
|
self.assertEqual(self.cluster_template.name, response['name'])
|
||||||
self.assertEqual(cluster_template.apiserver_port,
|
self.assertEqual(self.cluster_template.apiserver_port,
|
||||||
response['apiserver_port'])
|
response['apiserver_port'])
|
||||||
self.assertEqual(cluster_template.image_id,
|
self.assertEqual(self.cluster_template.image_id,
|
||||||
response['image_id'])
|
response['image_id'])
|
||||||
self.assertEqual(cluster_template.fixed_network,
|
self.assertEqual(self.cluster_template.fixed_network,
|
||||||
response['fixed_network'])
|
response['fixed_network'])
|
||||||
self.assertEqual(cluster_template.network_driver,
|
self.assertEqual(self.cluster_template.network_driver,
|
||||||
response['network_driver'])
|
response['network_driver'])
|
||||||
self.assertEqual(cluster_template.volume_driver,
|
self.assertEqual(self.cluster_template.volume_driver,
|
||||||
response['volume_driver'])
|
response['volume_driver'])
|
||||||
self.assertEqual(cluster_template.docker_volume_size,
|
self.assertEqual(self.cluster_template.docker_volume_size,
|
||||||
response['docker_volume_size'])
|
response['docker_volume_size'])
|
||||||
self.assertEqual(cluster_template.coe, response['coe'])
|
self.assertEqual(self.cluster_template.coe, response['coe'])
|
||||||
self.assertEqual(cluster_template.http_proxy, response['http_proxy'])
|
self.assertEqual(self.cluster_template.http_proxy,
|
||||||
self.assertEqual(cluster_template.https_proxy, response['https_proxy'])
|
response['http_proxy'])
|
||||||
self.assertEqual(cluster_template.no_proxy, response['no_proxy'])
|
self.assertEqual(self.cluster_template.https_proxy,
|
||||||
self.assertEqual(cluster_template.labels, response['labels'])
|
response['https_proxy'])
|
||||||
|
self.assertEqual(self.cluster_template.no_proxy, response['no_proxy'])
|
||||||
|
self.assertEqual(self.cluster_template.labels, response['labels'])
|
||||||
|
|
||||||
def test_remove_non_existent_property_fail(self):
|
def test_remove_non_existent_property_fail(self):
|
||||||
response = self.patch_json('/clustertemplates/%s' %
|
response = self.patch_json('/clustertemplates/%s' %
|
||||||
|
@ -443,10 +442,10 @@ class TestPatch(api_base.FunctionalTest):
|
||||||
self.assertTrue(response.json['errors'])
|
self.assertTrue(response.json['errors'])
|
||||||
|
|
||||||
def test_remove_mandatory_property_fail(self):
|
def test_remove_mandatory_property_fail(self):
|
||||||
mandatory_properties = ('/image_id', '/keypair_id',
|
mandatory_properties = ('/image_id', '/keypair_id', '/coe',
|
||||||
'/external_network_id', '/coe',
|
'/external_network_id', '/server_type',
|
||||||
'/tls_disabled', '/public',
|
'/tls_disabled', '/public',
|
||||||
'/registry_enabled', '/server_type',
|
'/registry_enabled',
|
||||||
'/cluster_distro', '/network_driver')
|
'/cluster_distro', '/network_driver')
|
||||||
for p in mandatory_properties:
|
for p in mandatory_properties:
|
||||||
response = self.patch_json('/clustertemplates/%s' %
|
response = self.patch_json('/clustertemplates/%s' %
|
||||||
|
|
|
@ -18,6 +18,7 @@ import mock
|
||||||
import six
|
import six
|
||||||
import webtest
|
import webtest
|
||||||
import wsme
|
import wsme
|
||||||
|
from wsme import types as wtypes
|
||||||
|
|
||||||
from magnum.api.controllers.v1 import types
|
from magnum.api.controllers.v1 import types
|
||||||
from magnum.common import exception
|
from magnum.common import exception
|
||||||
|
@ -61,12 +62,15 @@ class TestUuidType(base.FunctionalTest):
|
||||||
types.UuidType.validate, 'invalid-uuid')
|
types.UuidType.validate, 'invalid-uuid')
|
||||||
|
|
||||||
|
|
||||||
|
class MyBaseType(object):
|
||||||
|
"""Helper class, patched by objects of type MyPatchType"""
|
||||||
|
mandatory = wsme.wsattr(wtypes.text, mandatory=True)
|
||||||
|
|
||||||
|
|
||||||
class MyPatchType(types.JsonPatchType):
|
class MyPatchType(types.JsonPatchType):
|
||||||
"""Helper class for TestJsonPatchType tests."""
|
"""Helper class for TestJsonPatchType tests."""
|
||||||
|
_api_base = MyBaseType
|
||||||
@staticmethod
|
_extra_non_removable_attrs = {'/non_removable'}
|
||||||
def mandatory_attrs():
|
|
||||||
return ['/mandatory']
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def internal_attrs():
|
def internal_attrs():
|
||||||
|
@ -108,17 +112,33 @@ class TestJsonPatchType(base.FunctionalTest):
|
||||||
ret = self._patch_json(patch, True)
|
ret = self._patch_json(patch, True)
|
||||||
self.assertEqual(400, ret.status_int)
|
self.assertEqual(400, ret.status_int)
|
||||||
|
|
||||||
def test_mandatory_attr(self):
|
def test_cannot_remove_internal_attr(self):
|
||||||
patch = [{'op': 'replace', 'path': '/mandatory', 'value': 'foo'}]
|
patch = [{'path': '/internal', 'op': 'remove'}]
|
||||||
|
ret = self._patch_json(patch, True)
|
||||||
|
self.assertEqual(400, ret.status_int)
|
||||||
|
|
||||||
|
def test_cannot_add_internal_attr(self):
|
||||||
|
patch = [{'path': '/internal', 'op': 'add', 'value': 'foo'}]
|
||||||
|
ret = self._patch_json(patch, True)
|
||||||
|
self.assertEqual(400, ret.status_int)
|
||||||
|
|
||||||
|
def test_update_mandatory_attr(self):
|
||||||
|
patch = [{'path': '/mandatory', 'op': 'replace', 'value': 'foo'}]
|
||||||
ret = self._patch_json(patch, False)
|
ret = self._patch_json(patch, False)
|
||||||
self.assertEqual(200, ret.status_int)
|
self.assertEqual(200, ret.status_int)
|
||||||
self.assertEqual(patch, ret.json)
|
self.assertEqual(patch, ret.json)
|
||||||
|
|
||||||
def test_cannot_remove_mandatory_attr(self):
|
def test_cannot_remove_mandatory_attr(self):
|
||||||
patch = [{'op': 'remove', 'path': '/mandatory'}]
|
patch = [{'path': '/mandatory', 'op': 'remove'}]
|
||||||
ret = self._patch_json(patch, True)
|
ret = self._patch_json(patch, True)
|
||||||
self.assertEqual(400, ret.status_int)
|
self.assertEqual(400, ret.status_int)
|
||||||
|
|
||||||
|
def test_cannot_remove_extra_non_removable_attr(self):
|
||||||
|
patch = [{'path': '/non_removable', 'op': 'remove'}]
|
||||||
|
ret = self._patch_json(patch, True)
|
||||||
|
self.assertEqual(400, ret.status_int)
|
||||||
|
self.assertTrue(ret.json['faultstring'])
|
||||||
|
|
||||||
def test_missing_required_fields_path(self):
|
def test_missing_required_fields_path(self):
|
||||||
missing_path = [{'op': 'remove'}]
|
missing_path = [{'op': 'remove'}]
|
||||||
ret = self._patch_json(missing_path, True)
|
ret = self._patch_json(missing_path, True)
|
||||||
|
|
Loading…
Reference in New Issue