Commit 0b005a39 by Simon János

actually the whole code has been refactored

parent eed8e4b7
import copy
import json
import uuid
from enum import Enum
from voluptuous import Schema, Invalid, MultipleInvalid
from voluptuous import Schema, Invalid, MultipleInvalid, Any, ALLOW_EXTRA, PREVENT_EXTRA
class Resource(object):
DEFAULT_ID_LENGTH = 8
def __init__(self, *args, **kwargs):
if len(args):
resource = args[0]
if isinstance(resource, Resource):
self.__id = resource.id
self.__type = Resource.Type[resource.type]
else:
self.__attributes_from_dict(resource)
else:
self.__attributes_from_dict(kwargs)
def __attributes_from_dict(self, attributes):
schema = Schema({
'id': str,
'type': Resource.Type.validate
})
ADDITIONAL_SCHEMA = {}
def __new__(cls, *args, **kwargs):
attributes = cls.__attributes_from_dict(args[0] if len(args) else kwargs)
resource_type = attributes['type'].value
if resource_type is Resource:
raise InvalidResourceException('Resource class is abstract')
cls.__validate_attributes(attributes, cls.__schema(resource_type, extend=True))
instance = super(Resource, resource_type).__new__(resource_type)
instance._attributes = attributes # pylint: disable=protected-access
return instance
@classmethod
def __attributes_from_dict(cls, attributes):
cls.__validate_attributes(attributes, cls.__schema(extend=False))
attributes = copy.deepcopy(attributes)
attributes['id'] = attributes.get('id', Resource.__generate_random_id())
attributes['type'] = ResourceType.validate(attributes.get('type', cls))
return attributes
@classmethod
def __validate_attributes(cls, attributes, schema):
try:
schema(attributes)
self.__id = attributes.get('id', Resource._random_id())
self.__type = Resource.Type[attributes.get('type', 'null')]
except MultipleInvalid as invalid:
raise InvalidResourceException(invalid)
@property
def id(self):
return self.__id
@classmethod
def __schema(cls, resource_type=None, extend=False):
try:
string_validator = Any(str, unicode)
except NameError:
string_validator = Any(str)
schema = Schema(schema={'id': string_validator, 'type': ResourceType.validate}, extra=ALLOW_EXTRA)
if extend:
additional = resource_type.ADDITIONAL_SCHEMA if resource_type else cls.ADDITIONAL_SCHEMA
schema = schema.extend(additional, extra=PREVENT_EXTRA)
return schema
@property
def type(self):
return self.__type.name
return str(self._attributes['type'])
def __getattr__(self, item):
try:
return self._attributes[item]
except KeyError:
raise AttributeError('Attribute (%s) is not set on resource')
@staticmethod
def _random_id():
def __generate_random_id():
return uuid.uuid4().hex[:Resource.DEFAULT_ID_LENGTH]
def __eq__(self, other):
return isinstance(other, Resource) and \
self.id == other.id and \
self.type == other.type
try:
for attr, other_value in other.items():
value = self._attributes.get(attr)
if value is None or value != other_value:
return False
return True
except AttributeError:
return False
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, self.id)
return json.dumps(self, default=Resource.json_encoder)
def __hash__(self):
return hash(self.__repr__())
def __iter__(self):
return self._attributes.__iter__()
class Type(Enum):
null = 0
instance = 1
def keys(self):
return self._attributes.keys()
@classmethod
def validate(cls, value):
try:
cls[value]
except KeyError:
raise Invalid('The given resource type (%s) is not valid' % value)
return True
def values(self):
return self._attributes.values()
def items(self):
return self._attributes.items()
class Instance(Resource):
def __init__(self, *args, **kwargs):
kwargs['type'] = Resource.Type.instance.name
super(Instance, self).__init__(*args, **kwargs)
class ResourceGroup(object):
def __init__(self, *args):
self.__resources = set()
if len(args):
for argument in args:
try:
self.__iadd__(argument)
except InvalidResourceException:
for resource in argument:
self.__iadd__(resource)
def __getitem__(self, item):
return self._attributes[item]
def __add__(self, other):
return ResourceGroup(resources=[self, other])
@staticmethod
def json_encoder(data):
if isinstance(data, ResourceType):
return str(data)
return dict(data)
class ResourceGroup(Resource):
ADDITIONAL_SCHEMA = {
'resources': Any(list, set, tuple)
}
def __init__(self, *_, **__):
self.__resources = {}
if 'resources' in self._attributes.keys():
for resource in self._attributes['resources']:
self.add(resource)
self._attributes['resources'] = self.resources
def add(self, resource):
if not isinstance(resource, Resource):
try:
resource = Resource(resource)
except InvalidResourceException:
raise InvalidResourceException('ResourceGroup can only store resources')
self.__resources[resource.id] = resource
def remove(self, resource):
try:
del self.__resources[Resource(resource).id]
except InvalidResourceException:
del self.__resources[resource.id]
@property
def resources(self):
return list(self.__resources)
return list(self.__resources.values())
def __add__(self, other):
if isinstance(other, dict):
return ResourceGroup(Resource(other), *self.resources)
if isinstance(other, Resource):
return ResourceGroup(other, *self.resources)
if isinstance(other, ResourceGroup):
resources = other.resources + self.resources
return ResourceGroup(*resources)
raise InvalidResourceException()
def __iadd__(self, other):
if isinstance(other, dict):
self.__resources.add(Resource(other))
return self
if isinstance(other, Resource):
self.__resources.add(other)
return self
if isinstance(other, ResourceGroup):
self.__resources.union(other.resources)
return self
raise InvalidResourceException()
def __iter__(self):
return self.__resources.__iter__()
class Instance(Resource):
ADDITIONAL_SCHEMA = {
'number_of_cores': int
}
def __eq__(self, other):
return self.__resources == set(other.resources)
class ResourceType(Enum):
null = Resource
group = ResourceGroup
instance = Instance
@classmethod
def validate(cls, value):
if isinstance(value, cls):
return value
try:
return cls[value]
except KeyError:
return cls(value)
except ValueError:
raise Invalid('The given resource type (%s) is not valid' % value)
def __str__(self):
return str(self.name)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, str(self))
class InvalidResourceException(Exception):
......
import json
from unittest.case import TestCase
from voluptuous import Invalid
from orchestrator.model.resources import Resource, ResourceGroup, Instance, InvalidResourceException
from orchestrator.model.resources import Resource, ResourceGroup, Instance, InvalidResourceException, ResourceType
class ResourceTest(TestCase):
def test_resource_without_args(self):
# when
resource = Resource()
# then
self.assertIsInstance(resource.id, str)
self.assertEqual(Resource.DEFAULT_ID_LENGTH, len(resource.id))
self.assertEqual('null', resource.type)
with self.assertRaises(InvalidResourceException):
Resource()
def test_resource_from_args(self):
# given
expected_id = 'dummy_resource'
expected_type = 'null'
expected_type = ResourceType.instance
# when
result = Resource(id=expected_id, type=expected_type)
# then
self.assertEqual(expected_id, result.id)
self.assertEqual(expected_type, result.type)
self.assertEqual(expected_type.name, result.type)
def test_resource_from_dict(self):
# given
resource_dict = dict(id='dummy_resource', type='null')
resource_dict = {'id': 'dummy_resource', 'type': 'instance'}
# when
result = Resource(resource_dict)
......@@ -50,12 +44,20 @@ class ResourceTest(TestCase):
with self.assertRaises(InvalidResourceException):
Resource(invalid_attributes)
def test_resource_type_validate(self):
self.assertTrue(Resource.Type.validate('instance'))
def test_resource_type_validation(self):
self.assertTrue(ResourceType.validate('instance'))
self.assertTrue(ResourceType.validate(Instance))
self.assertTrue(ResourceType.validate(ResourceType.instance))
self.assertIsInstance(Resource(type='instance'), Instance)
self.assertIsInstance(Resource(type=Instance), Instance)
self.assertIsInstance(Resource(type=ResourceType.instance), Instance)
self.assertEqual(Instance(id='some_instance'), Resource(id='some_instance', type='instance'))
self.assertEqual(Instance(id='some_instance'), Resource(id='some_instance', type=Instance))
self.assertEqual(Instance(id='some_instance'), Resource(id='some_instance', type=ResourceType.instance))
def test_resource_type_invalid(self):
with self.assertRaises(Invalid):
Resource.Type.validate('nonexisting_resource_type')
with self.assertRaises(ValueError):
ResourceType.validate('nonexisting_resource_type')
def test_resource_instance(self):
# when
......@@ -64,20 +66,61 @@ class ResourceTest(TestCase):
# then
self.assertIsInstance(instance.id, str)
self.assertEqual(Resource.DEFAULT_ID_LENGTH, len(instance.id))
self.assertEqual(Resource.Type.instance.name, instance.type)
self.assertEqual(ResourceType.instance.name, instance.type)
def test_resource_equivalence(self):
# given
resource1 = Resource(id='some_resource', type='null')
resource2 = Resource(id='some_resource', type='null')
resource3 = Resource(id='some_other_resource', type='null')
resource4 = Resource(id='some_resource', type='instance')
resource1 = Resource(id='some_resource', type=Instance)
resource2 = Resource(id='some_resource', type=Instance)
resource3 = Instance(id='some_resource')
resource4 = Resource(id='some_other_resource', type=Instance)
resource5 = Resource(id='some_resource', type=ResourceGroup)
# then
self.assertEqual(resource1, resource1)
self.assertEqual(resource1, resource2)
self.assertNotEqual(resource1, resource3)
self.assertEqual(resource1, resource3)
self.assertNotEqual(resource1, resource4)
self.assertNotEqual(resource1, resource5)
self.assertFalse(resource1 == 500)
def test_resource_str_representation(self):
# given
expected_json = '{"id": "some_resource", "type": "instance"}'
expected_dict = json.loads(expected_json)
# when
resource = Resource(expected_dict)
resource_dict = json.loads(str(resource))
# then
self.assertEqual(expected_dict, resource_dict)
def test_resource_dict_representation(self):
# given
expected_dict = {'id': 'some_resource', 'type': ResourceType.instance}
# when
resource = Resource(expected_dict)
resource_dict = dict(resource)
# then
self.assertEqual(expected_dict, resource_dict)
def test_dynamic_resource_type(self):
# when
resource = Resource(type='instance')
# then
self.__assert_is_instance(resource)
self.__assert_is_instance(Resource({'type': 'instance'}))
with self.assertRaises(InvalidResourceException):
Resource()
def __assert_is_instance(self, resource):
self.assertIsInstance(resource, Instance)
self.assertEqual(ResourceType.instance.name, resource.type)
class ResourceGroupTest(TestCase):
......@@ -90,65 +133,119 @@ class ResourceGroupTest(TestCase):
def test_resource_group_from_resources(self):
# given
resource1 = Resource(id='some_resource')
resource2 = Resource(id='some_other_resource')
resource1 = Instance(id='some_resource')
resource2 = Instance(id='some_other_resource')
# then
self.assertEqual([resource1], ResourceGroup(resource1).resources)
self.assertEqual([resource1], ResourceGroup([resource1]).resources)
self.assertEqual([resource1], ResourceGroup([resource1, resource1]).resources)
self.assertEqual([resource1], ResourceGroup(resources=[resource1]).resources)
self.assertEqual([resource1], ResourceGroup(resources=[resource1, resource1]).resources)
for resources in [[resource1, resource2], [resource2, resource1]]:
self.assertEqual(2, len(ResourceGroup(*resources).resources))
self.assertIn(resource1, ResourceGroup(*resources).resources)
self.assertIn(resource2, ResourceGroup(*resources).resources)
self.assertEqual(2, len(ResourceGroup(resources=resources).resources))
self.assertIn(resource1, ResourceGroup(resources=resources).resources)
self.assertIn(resource2, ResourceGroup(resources=resources).resources)
def test_resource_group_from_invalid_collection(self):
with self.assertRaises(InvalidResourceException):
ResourceGroup(resources={'invalid': 'resource'})
def test_resource_group_from_dicts(self):
# given
dict1 = dict(id='some_resource')
dict2 = dict(id='some_other_resource')
expected1 = Resource(dict1)
expected2 = Resource(dict2)
dict1 = dict(id='some_resource', type=Instance)
dict2 = dict(id='some_other_resource', type=Instance)
expected1 = Instance(dict1)
expected2 = Instance(dict2)
# then
self.assertEqual([expected1], ResourceGroup(dict1).resources)
self.assertEqual([expected1], ResourceGroup([dict1]).resources)
self.assertEqual([expected1], ResourceGroup([dict1, dict1]).resources)
self.assertEqual([expected1], ResourceGroup(resources=[dict1]).resources)
self.assertEqual([expected1], ResourceGroup(resources=[dict1, dict1]).resources)
for resources in [[expected1, expected2], [dict2, dict1]]:
self.assertEqual(2, len(ResourceGroup(resources).resources))
self.assertIn(expected1, ResourceGroup(resources).resources)
self.assertIn(expected2, ResourceGroup(resources).resources)
self.assertEqual(2, len(ResourceGroup(resources=resources).resources))
self.assertIn(expected1, ResourceGroup(resources=resources).resources)
self.assertIn(expected2, ResourceGroup(resources=resources).resources)
def test_resource_group_from_dict(self):
# given
expected_group_id = 'some_resource_group'
expected_resource_id = 'some_instance'
group_dict = {
'id': expected_group_id,
'type': 'group',
'resources': [
{'id': expected_resource_id, 'type': 'instance'}
]
}
# when
group = ResourceGroup(group_dict)
# then
self.assertEqual(expected_group_id, group.id)
self.assertEqual(1, len(group.resources))
self.assertEqual(expected_resource_id, group.resources[0].id)
def test_resource_group_add_resource(self):
# given
group = ResourceGroup()
resource = Instance()
# when
group.add(resource)
# then
self.assertEqual(1, len(group.resources))
self.assertIn(resource, group.resources)
def test_resource_group_remove_resource(self):
# given
resource = Instance()
group = ResourceGroup()
group.add(resource)
# when
group.remove(resource)
# then
self.assertEqual([], group.resources)
# @skip('works same way as resource addition')
def test_resource_group_addition(self):
# given
resource1 = Resource()
resource2 = Resource()
group1 = ResourceGroup(resource1)
group2 = ResourceGroup(resource2)
group1 = ResourceGroup()
group2 = ResourceGroup()
resource = Instance()
# when
result_group = group1 + group2
result_group1 = group1 + group2
result_group2 = result_group1 + resource
# then
self.assertEqual(2, len(result_group.resources))
self.assertIn(resource1, result_group.resources)
self.assertIn(resource2, result_group.resources)
self.assertIsInstance(result_group1, ResourceGroup)
self.assertIsInstance(result_group2, ResourceGroup)
self.assertEqual(2, len(result_group1.resources))
self.assertEqual(2, len(result_group2.resources))
self.assertIn(group1, result_group1.resources)
self.assertIn(group2, result_group1.resources)
self.assertIn(resource, result_group2.resources)
def test_resource_group_iteration(self):
# when
group = ResourceGroup(Resource(), Resource(), Resource())
group = ResourceGroup(resources=[Instance(), Instance(), Instance()])
# then
for index, resource in enumerate(group):
for index, resource in enumerate(group.resources):
self.assertEqual(group.resources[index], resource)
def test_resource_group_equivalence(self):
# given
resource1 = Resource(id='some_resource', type='null')
resource2 = Resource(id='some_resource', type='null')
resource3 = Resource(id='some_other_resource', type='null')
resource1 = Instance(id='some_resource')
resource2 = Instance(id='some_other_resource')
# then
self.assertEqual(ResourceGroup(), ResourceGroup())
self.assertEqual(ResourceGroup(resource1), ResourceGroup(resource1))
self.assertEqual(ResourceGroup(resource1), ResourceGroup(resource2))
self.assertNotEqual(ResourceGroup(resource1), ResourceGroup(resource3))
self.assertEqual(ResourceGroup(id='some_group'),
ResourceGroup(id='some_group'))
self.assertEqual(ResourceGroup(id='some_group', resources=[resource1]),
ResourceGroup(id='some_group', resources=[resource1]))
self.assertNotEqual(ResourceGroup(id='some_group'),
ResourceGroup(id='some_other_group'))
self.assertNotEqual(ResourceGroup(id='some_group', resources=[resource1]),
ResourceGroup(id='some_group', resources=[resource2]))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment