Commit 0b005a39 by Simon János

actually the whole code has been refactored

parent eed8e4b7
import copy
import json
import uuid import uuid
from enum import Enum from enum import Enum
from voluptuous import Schema, Invalid, MultipleInvalid from voluptuous import Schema, Invalid, MultipleInvalid, Any, ALLOW_EXTRA, PREVENT_EXTRA
class Resource(object): class Resource(object):
DEFAULT_ID_LENGTH = 8 DEFAULT_ID_LENGTH = 8
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
def __init__(self, *args, **kwargs): @classmethod
if len(args): def __attributes_from_dict(cls, attributes):
resource = args[0] cls.__validate_attributes(attributes, cls.__schema(extend=False))
if isinstance(resource, Resource): attributes = copy.deepcopy(attributes)
self.__id = resource.id attributes['id'] = attributes.get('id', Resource.__generate_random_id())
self.__type = Resource.Type[resource.type] attributes['type'] = ResourceType.validate(attributes.get('type', cls))
else: return attributes
self.__attributes_from_dict(resource)
else: @classmethod
self.__attributes_from_dict(kwargs) def __validate_attributes(cls, attributes, schema):
def __attributes_from_dict(self, attributes):
schema = Schema({
'id': str,
'type': Resource.Type.validate
})
try: try:
schema(attributes) schema(attributes)
self.__id = attributes.get('id', Resource._random_id())
self.__type = Resource.Type[attributes.get('type', 'null')]
except MultipleInvalid as invalid: except MultipleInvalid as invalid:
raise InvalidResourceException(invalid) raise InvalidResourceException(invalid)
@property @classmethod
def id(self): def __schema(cls, resource_type=None, extend=False):
return self.__id 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 @property
def type(self): 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 @staticmethod
def _random_id(): def __generate_random_id():
return uuid.uuid4().hex[:Resource.DEFAULT_ID_LENGTH] return uuid.uuid4().hex[:Resource.DEFAULT_ID_LENGTH]
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, Resource) and \ try:
self.id == other.id and \ for attr, other_value in other.items():
self.type == other.type value = self._attributes.get(attr)
if value is None or value != other_value:
return False
return True
except AttributeError:
return False
def __repr__(self): def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, self.id) return json.dumps(self, default=Resource.json_encoder)
def __hash__(self): def __iter__(self):
return hash(self.__repr__()) return self._attributes.__iter__()
class Type(Enum): def keys(self):
null = 0 return self._attributes.keys()
instance = 1
@classmethod def values(self):
def validate(cls, value): return self._attributes.values()
try:
cls[value]
except KeyError:
raise Invalid('The given resource type (%s) is not valid' % value)
return True
def items(self):
return self._attributes.items()
class Instance(Resource): def __getitem__(self, item):
def __init__(self, *args, **kwargs): return self._attributes[item]
kwargs['type'] = Resource.Type.instance.name
super(Instance, self).__init__(*args, **kwargs)
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
class ResourceGroup(object): def remove(self, resource):
def __init__(self, *args):
self.__resources = set()
if len(args):
for argument in args:
try: try:
self.__iadd__(argument) del self.__resources[Resource(resource).id]
except InvalidResourceException: except InvalidResourceException:
for resource in argument: del self.__resources[resource.id]
self.__iadd__(resource)
@property @property
def resources(self): 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): class Instance(Resource):
return self.__resources.__iter__() 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): class InvalidResourceException(Exception):
......
import json
from unittest.case import TestCase from unittest.case import TestCase
from voluptuous import Invalid from orchestrator.model.resources import Resource, ResourceGroup, Instance, InvalidResourceException, ResourceType
from orchestrator.model.resources import Resource, ResourceGroup, Instance, InvalidResourceException
class ResourceTest(TestCase): class ResourceTest(TestCase):
def test_resource_without_args(self): def test_resource_without_args(self):
# when with self.assertRaises(InvalidResourceException):
resource = Resource() Resource()
# then
self.assertIsInstance(resource.id, str)
self.assertEqual(Resource.DEFAULT_ID_LENGTH, len(resource.id))
self.assertEqual('null', resource.type)
def test_resource_from_args(self): def test_resource_from_args(self):
# given # given
expected_id = 'dummy_resource' expected_id = 'dummy_resource'
expected_type = 'null' expected_type = ResourceType.instance
# when # when
result = Resource(id=expected_id, type=expected_type) result = Resource(id=expected_id, type=expected_type)
# then # then
self.assertEqual(expected_id, result.id) 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): def test_resource_from_dict(self):
# given # given
resource_dict = dict(id='dummy_resource', type='null') resource_dict = {'id': 'dummy_resource', 'type': 'instance'}
# when # when
result = Resource(resource_dict) result = Resource(resource_dict)
...@@ -50,12 +44,20 @@ class ResourceTest(TestCase): ...@@ -50,12 +44,20 @@ class ResourceTest(TestCase):
with self.assertRaises(InvalidResourceException): with self.assertRaises(InvalidResourceException):
Resource(invalid_attributes) Resource(invalid_attributes)
def test_resource_type_validate(self): def test_resource_type_validation(self):
self.assertTrue(Resource.Type.validate('instance')) 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): def test_resource_type_invalid(self):
with self.assertRaises(Invalid): with self.assertRaises(ValueError):
Resource.Type.validate('nonexisting_resource_type') ResourceType.validate('nonexisting_resource_type')
def test_resource_instance(self): def test_resource_instance(self):
# when # when
...@@ -64,20 +66,61 @@ class ResourceTest(TestCase): ...@@ -64,20 +66,61 @@ class ResourceTest(TestCase):
# then # then
self.assertIsInstance(instance.id, str) self.assertIsInstance(instance.id, str)
self.assertEqual(Resource.DEFAULT_ID_LENGTH, len(instance.id)) 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): def test_resource_equivalence(self):
# given # given
resource1 = Resource(id='some_resource', type='null') resource1 = Resource(id='some_resource', type=Instance)
resource2 = Resource(id='some_resource', type='null') resource2 = Resource(id='some_resource', type=Instance)
resource3 = Resource(id='some_other_resource', type='null') resource3 = Instance(id='some_resource')
resource4 = Resource(id='some_resource', type='instance') resource4 = Resource(id='some_other_resource', type=Instance)
resource5 = Resource(id='some_resource', type=ResourceGroup)
# then # then
self.assertEqual(resource1, resource1) self.assertEqual(resource1, resource1)
self.assertEqual(resource1, resource2) self.assertEqual(resource1, resource2)
self.assertNotEqual(resource1, resource3) self.assertEqual(resource1, resource3)
self.assertNotEqual(resource1, resource4) 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): class ResourceGroupTest(TestCase):
...@@ -90,65 +133,119 @@ class ResourceGroupTest(TestCase): ...@@ -90,65 +133,119 @@ class ResourceGroupTest(TestCase):
def test_resource_group_from_resources(self): def test_resource_group_from_resources(self):
# given # given
resource1 = Resource(id='some_resource') resource1 = Instance(id='some_resource')
resource2 = Resource(id='some_other_resource') resource2 = Instance(id='some_other_resource')
# then # then
self.assertEqual([resource1], ResourceGroup(resource1).resources) self.assertEqual([resource1], ResourceGroup(resources=[resource1]).resources)
self.assertEqual([resource1], ResourceGroup([resource1]).resources) self.assertEqual([resource1], ResourceGroup(resources=[resource1, resource1]).resources)
self.assertEqual([resource1], ResourceGroup([resource1, resource1]).resources)
for resources in [[resource1, resource2], [resource2, resource1]]: for resources in [[resource1, resource2], [resource2, resource1]]:
self.assertEqual(2, len(ResourceGroup(*resources).resources)) self.assertEqual(2, len(ResourceGroup(resources=resources).resources))
self.assertIn(resource1, ResourceGroup(*resources).resources) self.assertIn(resource1, ResourceGroup(resources=resources).resources)
self.assertIn(resource2, ResourceGroup(*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): def test_resource_group_from_dicts(self):
# given # given
dict1 = dict(id='some_resource') dict1 = dict(id='some_resource', type=Instance)
dict2 = dict(id='some_other_resource') dict2 = dict(id='some_other_resource', type=Instance)
expected1 = Resource(dict1) expected1 = Instance(dict1)
expected2 = Resource(dict2) expected2 = Instance(dict2)
# then # then
self.assertEqual([expected1], ResourceGroup(dict1).resources) self.assertEqual([expected1], ResourceGroup(resources=[dict1]).resources)
self.assertEqual([expected1], ResourceGroup([dict1]).resources) self.assertEqual([expected1], ResourceGroup(resources=[dict1, dict1]).resources)
self.assertEqual([expected1], ResourceGroup([dict1, dict1]).resources)
for resources in [[expected1, expected2], [dict2, dict1]]: for resources in [[expected1, expected2], [dict2, dict1]]:
self.assertEqual(2, len(ResourceGroup(resources).resources)) self.assertEqual(2, len(ResourceGroup(resources=resources).resources))
self.assertIn(expected1, ResourceGroup(resources).resources) self.assertIn(expected1, ResourceGroup(resources=resources).resources)
self.assertIn(expected2, ResourceGroup(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): def test_resource_group_addition(self):
# given # given
resource1 = Resource() group1 = ResourceGroup()
resource2 = Resource() group2 = ResourceGroup()
group1 = ResourceGroup(resource1) resource = Instance()
group2 = ResourceGroup(resource2)
# when # when
result_group = group1 + group2 result_group1 = group1 + group2
result_group2 = result_group1 + resource
# then # then
self.assertEqual(2, len(result_group.resources)) self.assertIsInstance(result_group1, ResourceGroup)
self.assertIn(resource1, result_group.resources) self.assertIsInstance(result_group2, ResourceGroup)
self.assertIn(resource2, result_group.resources) 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): def test_resource_group_iteration(self):
# when # when
group = ResourceGroup(Resource(), Resource(), Resource()) group = ResourceGroup(resources=[Instance(), Instance(), Instance()])
# then # then
for index, resource in enumerate(group): for index, resource in enumerate(group.resources):
self.assertEqual(group.resources[index], resource) self.assertEqual(group.resources[index], resource)
def test_resource_group_equivalence(self): def test_resource_group_equivalence(self):
# given # given
resource1 = Resource(id='some_resource', type='null') resource1 = Instance(id='some_resource')
resource2 = Resource(id='some_resource', type='null') resource2 = Instance(id='some_other_resource')
resource3 = Resource(id='some_other_resource', type='null')
# then # then
self.assertEqual(ResourceGroup(), ResourceGroup()) self.assertEqual(ResourceGroup(id='some_group'),
self.assertEqual(ResourceGroup(resource1), ResourceGroup(resource1)) ResourceGroup(id='some_group'))
self.assertEqual(ResourceGroup(resource1), ResourceGroup(resource2)) self.assertEqual(ResourceGroup(id='some_group', resources=[resource1]),
self.assertNotEqual(ResourceGroup(resource1), ResourceGroup(resource3)) 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