trove/trove/tests/unittests/instance/test_instance_controller.py

365 lines
15 KiB
Python

# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# 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 jsonschema
from mock import Mock
from testtools.matchers import Is, Equals
from testtools.testcase import skip
from trove.common import apischema
from trove.instance.service import InstanceController
from trove.tests.unittests import trove_testtools
class TestInstanceController(trove_testtools.TestCase):
def setUp(self):
super(TestInstanceController, self).setUp()
self.controller = InstanceController()
self.locality = 'affinity'
self.instance = {
"instance": {
"volume": {"size": "1"},
"users": [
{"name": "user1",
"password": "litepass",
"databases": [{"name": "firstdb"}]}
],
"flavorRef": "https://localhost:8779/v1.0/2500/1",
"name": "TEST-XYS2d2fe2kl;zx;jkl2l;sjdcma239(E)@(D",
"databases": [
{
"name": "firstdb",
"collate": "latin2_general_ci",
"character_set": "latin2"
},
{
"name": "db2"
}
],
"locality": self.locality
}
}
self.context = trove_testtools.TroveTestContext(self)
self.req = Mock(remote_addr='ip:port', host='myhost')
def verify_errors(self, errors, msg=None, properties=None, path=None):
msg = msg or []
properties = properties or []
self.assertThat(len(errors), Is(len(msg)))
i = 0
while i < len(msg):
self.assertIn(errors[i].message, msg)
if path:
self.assertThat(path, Equals(properties[i]))
else:
self.assertThat(errors[i].path.pop(), Equals(properties[i]))
i += 1
def test_get_schema_create(self):
schema = self.controller.get_schema('create', {'instance': {}})
self.assertIsNotNone(schema)
self.assertIn('instance', schema['properties'])
def test_get_schema_action_restart(self):
schema = self.controller.get_schema('action', {'restart': {}})
self.assertIsNotNone(schema)
self.assertIn('restart', schema['properties'])
def test_get_schema_action_resize_volume(self):
schema = self.controller.get_schema(
'action', {'resize': {'volume': {}}})
self.assertIsNotNone(schema)
self.assertIn('resize', schema['properties'])
self.assertIn(
'volume', schema['properties']['resize']['properties'])
def test_get_schema_action_resize_flavorRef(self):
schema = self.controller.get_schema(
'action', {'resize': {'flavorRef': {}}})
self.assertIsNotNone(schema)
self.assertIn('resize', schema['properties'])
self.assertIn(
'flavorRef', schema['properties']['resize']['properties'])
def test_get_schema_action_other(self):
schema = self.controller.get_schema(
'action', {'supersized': {'flavorRef': {}}})
self.assertIsNotNone(schema)
self.assertThat(len(schema.keys()), Is(0))
def test_validate_create_complete(self):
body = self.instance
schema = self.controller.get_schema('create', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_create_complete_with_restore(self):
body = self.instance
body['instance']['restorePoint'] = {
"backupRef": "d761edd8-0771-46ff-9743-688b9e297a3b"
}
schema = self.controller.get_schema('create', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_create_complete_with_restore_error(self):
body = self.instance
backup_id_ref = "invalid-backup-id-ref"
body['instance']['restorePoint'] = {
"backupRef": backup_id_ref
}
schema = self.controller.get_schema('create', body)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
self.assertThat(len(errors), Is(1))
self.assertThat(errors[0].message,
Equals("'%s' does not match '%s'" %
(backup_id_ref, apischema.uuid['pattern'])))
def test_validate_create_blankname(self):
body = self.instance
body['instance']['name'] = " "
schema = self.controller.get_schema('create', body)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
self.assertThat(len(errors), Is(1))
self.assertThat(errors[0].message,
Equals("' ' does not match '^.*[0-9a-zA-Z]+.*$'"))
def test_validate_create_invalid_name(self):
body = self.instance
body['instance']['name'] = "$#$%^^"
schema = self.controller.get_schema('create', body)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
self.assertEqual(1, len(errors))
self.assertIn("'$#$%^^' does not match '^.*[0-9a-zA-Z]+.*$'",
errors[0].message)
def test_validate_create_invalid_locality(self):
body = self.instance
body['instance']['locality'] = "$%^"
schema = self.controller.get_schema('create', body)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
error_messages = [error.message for error in errors]
error_paths = [error.path.pop() for error in errors]
self.assertEqual(1, len(errors))
self.assertIn("'$%^' does not match '^.*[0-9a-zA-Z]+.*$'",
error_messages)
self.assertIn("locality", error_paths)
def test_validate_restart(self):
body = {"restart": {}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_invalid_action(self):
# TODO(juice) perhaps we should validate the schema not recognized
body = {"restarted": {}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_resize_volume(self):
body = {"resize": {"volume": {"size": 4}}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_resize_volume_string(self):
body = {"resize": {"volume": {"size": "4"}}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_resize_volume_string_start_with_zero(self):
body = {"resize": {"volume": {"size": "0040"}}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_resize_volume_string_invalid_number(self):
body = {"resize": {"volume": {"size": '-44.0'}}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
self.assertThat(errors[0].context[1].message,
Equals("'-44.0' does not match '^0*[1-9]+[0-9]*$'"))
self.assertThat(errors[0].path.pop(), Equals('size'))
def test_validate_resize_volume_invalid_characters(self):
body = {"resize": {"volume": {"size": 'x'}}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
self.assertThat(errors[0].context[0].message,
Equals("'x' is not of type 'integer'"))
self.assertThat(errors[0].context[1].message,
Equals("'x' does not match '^0*[1-9]+[0-9]*$'"))
self.assertThat(errors[0].path.pop(), Equals('size'))
def test_validate_resize_volume_zero_number(self):
body = {"resize": {"volume": {"size": 0}}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
self.assertThat(errors[0].context[0].message,
Equals("0 is less than the minimum of 1"))
self.assertThat(errors[0].path.pop(), Equals('size'))
def test_validate_resize_volume_string_zero_number(self):
body = {"resize": {"volume": {"size": '0'}}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
self.assertThat(errors[0].context[1].message,
Equals("'0' does not match '^0*[1-9]+[0-9]*$'"))
self.assertThat(errors[0].path.pop(), Equals('size'))
def test_validate_resize_instance(self):
body = {"resize": {"flavorRef": "https://endpoint/v1.0/123/flavors/2"}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_resize_instance_int(self):
body = {"resize": {"flavorRef": 2}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_resize_instance_string(self):
body = {"resize": {"flavorRef": 'foo'}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertTrue(validator.is_valid(body))
def test_validate_resize_instance_empty_url(self):
body = {"resize": {"flavorRef": ""}}
schema = self.controller.get_schema('action', body)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
self.verify_errors(errors[0].context,
["'' is too short",
"'' does not match '^.*[0-9a-zA-Z]+.*$'",
"'' is not of type 'integer'"],
["flavorRef", "flavorRef", "flavorRef",
"flavorRef"],
errors[0].path.pop())
@skip("This URI validator allows just about anything you give it")
def test_validate_resize_instance_invalid_url(self):
body = {"resize": {"flavorRef": "xyz-re1f2-daze329d-f23901"}}
schema = self.controller.get_schema('action', body)
self.assertIsNotNone(schema)
validator = jsonschema.Draft4Validator(schema)
self.assertFalse(validator.is_valid(body))
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
self.verify_errors(errors, ["'' is too short"], ["flavorRef"])
def _setup_modify_instance_mocks(self):
instance = Mock()
instance.detach_replica = Mock()
instance.attach_configuration = Mock()
instance.detach_configuration = Mock()
instance.update_db = Mock()
return instance
def test_modify_instance_with_empty_args(self):
instance = self._setup_modify_instance_mocks()
args = {}
self.controller._modify_instance(self.context, self.req,
instance, **args)
self.assertEqual(0, instance.detach_replica.call_count)
self.assertEqual(0, instance.detach_configuration.call_count)
self.assertEqual(0, instance.attach_configuration.call_count)
self.assertEqual(0, instance.update_db.call_count)
def test_modify_instance_with_nonempty_args_calls_update_db(self):
instance = self._setup_modify_instance_mocks()
args = {}
args['any'] = 'anything'
self.controller._modify_instance(self.context, self.req,
instance, **args)
instance.update_db.assert_called_once_with(**args)
def test_modify_instance_with_False_detach_replica_arg(self):
instance = self._setup_modify_instance_mocks()
args = {}
args['detach_replica'] = False
self.controller._modify_instance(self.context, self.req,
instance, **args)
self.assertEqual(0, instance.detach_replica.call_count)
def test_modify_instance_with_True_detach_replica_arg(self):
instance = self._setup_modify_instance_mocks()
args = {}
args['detach_replica'] = True
self.controller._modify_instance(self.context, self.req,
instance, **args)
self.assertEqual(1, instance.detach_replica.call_count)
def test_modify_instance_with_configuration_id_arg(self):
instance = self._setup_modify_instance_mocks()
args = {}
args['configuration_id'] = 'some_id'
self.controller._modify_instance(self.context, self.req,
instance, **args)
self.assertEqual(1, instance.attach_configuration.call_count)
def test_modify_instance_with_None_configuration_id_arg(self):
instance = self._setup_modify_instance_mocks()
args = {}
args['configuration_id'] = None
self.controller._modify_instance(self.context, self.req,
instance, **args)
self.assertEqual(1, instance.detach_configuration.call_count)
def test_modify_instance_with_all_args(self):
instance = self._setup_modify_instance_mocks()
args = {}
args['detach_replica'] = True
args['configuration_id'] = 'some_id'
self.controller._modify_instance(self.context, self.req,
instance, **args)
self.assertEqual(1, instance.detach_replica.call_count)
self.assertEqual(1, instance.attach_configuration.call_count)
instance.update_db.assert_called_once_with(**args)