charms.openstack/unit_tests/test_charms_openstack_adapt...

1166 lines
46 KiB
Python

# Copyright 2016 Canonical Ltd
#
# 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.
# Note that the unit_tests/__init__.py has the following lines to stop
# side effects from the imorts from charm helpers.
# sys.path.append('./lib')
# mock out some charmhelpers libraries as they have apt install side effects
# sys.modules['charmhelpers.contrib.openstack.utils'] = mock.MagicMock()
# sys.modules['charmhelpers.contrib.network.ip'] = mock.MagicMock()
import copy
import unittest
import mock
import charms_openstack.adapters as adapters
class TestCustomProperties(unittest.TestCase):
def test_adapter_property(self):
with mock.patch.object(adapters, '_custom_adapter_properties', new={}):
@adapters.adapter_property('my-int')
def test_func():
pass
self.assertTrue(adapters._custom_adapter_properties['my-int'],
test_func)
def test_config_property(self):
with mock.patch.object(adapters, '_custom_config_properties', new={}):
@adapters.config_property
def test_func():
pass
self.assertTrue(adapters._custom_config_properties['test_func'],
test_func)
class MyRelation(object):
auto_accessors = ['this', 'that']
relation_name = 'my-name'
def this(self):
return 'this'
def that(self):
return 'that'
def some(self):
return 'thing'
class TestOpenStackRelationAdapter(unittest.TestCase):
def test_class(self):
ad = adapters.OpenStackRelationAdapter(MyRelation(), ['some'])
self.assertEqual(ad.this, 'this')
self.assertEqual(ad.that, 'that')
self.assertEqual(ad.some, 'thing')
self.assertEqual(ad.relation_name, 'my-name')
with self.assertRaises(AttributeError):
ad.relation_name = 'hello'
def test_class_no_relation(self):
ad = adapters.OpenStackRelationAdapter(relation_name='cluster')
self.assertEqual(ad.relation_name, 'cluster')
def test_make_default_relation_adapter(self):
# test that no properties just gets the standard one.
self.assertEqual(
adapters.make_default_relation_adapter('fake', None, {}),
'fake')
# now create a fake class with some properties to work with
class FakeRelation(object):
a = 4
def b(int):
return int.a # e.g. in test, return the 4 for the property 'b'
kls = adapters.make_default_relation_adapter(
FakeRelation, 'my./?-int', {'b': b})
self.assertEqual(kls.__name__, 'MyIntRelationAdapterModified')
i = kls()
self.assertIsInstance(i, FakeRelation)
self.assertEqual(i.b, 4)
class FakeRabbitMQRelation():
auto_accessors = ['vip', 'private_address']
relation_name = 'amqp'
def __init__(self, vip=None):
self._vip = vip
def vip(self):
return self._vip
def private_address(self):
return 'private-address'
def rabbitmq_hosts(self):
return ['host1', 'host2']
def vhost(self):
return 'vhost'
def username(self):
return 'fakename'
class TestRabbitMQRelationAdapter(unittest.TestCase):
def test_class(self):
fake = FakeRabbitMQRelation(None)
mq = adapters.RabbitMQRelationAdapter(fake)
self.assertEqual(mq.vhost, 'vhost')
self.assertEqual(mq.username, 'fakename')
self.assertEqual(mq.host, 'private-address')
# TODO: can't do the following 2 lines as not dynamic accessors
# fake._vip = 'vip1'
# self.assertEqual(mq.host, 'vip1')
self.assertEqual(mq.hosts, 'host1,host2')
class FakeAPIConfigAdapter():
@property
def local_address(self):
return 'this_unit_private_addr'
@property
def local_unit_name(self):
return 'this_unit-1'
class FakePeerRelation():
auto_accessors = ['private_address']
relation_name = 'cluster'
def ip_map(self, address_key=None):
if not address_key:
address_key = 'default'
addresses = {
'public-address': [
('peer_unit-1', 'peer_unit1_public_addr'),
('peer_unit-2', 'peer_unit2_public_addr')],
'internal-address': [
('peer_unit-1', 'peer_unit1_internal_addr'),
('peer_unit-2', 'peer_unit2_internal_addr')],
'admin-address': [
('peer_unit-1', 'peer_unit1_admin_addr'),
('peer_unit-2', 'peer_unit2_admin_addr')],
'default': [
('peer_unit-1', 'peer_unit1_private_addr'),
('peer_unit-2', 'peer_unit2_private_addr')],
}
return addresses[address_key]
class TestPeerHARelationAdapter(unittest.TestCase):
def test_class(self):
test_config = {
'os-public-network': 'public_network',
'os-admin-network': 'admin_network',
'os-internal-network': 'internal_network',
}
test_addresses = {
'public_network': 'this_unit_public_addr',
'admin_network': 'this_unit_admin_addr',
'internal_network': 'this_unit_internal_addr',
}
test_netmasks = {
'this_unit_public_addr': 'public_netmask',
'this_unit_admin_addr': 'admin_netmask',
'this_unit_internal_addr': 'internal_netmask',
'this_unit_private_addr': 'private_netmask',
}
expect_full = {
'this_unit_admin_addr': {
'backends': {
'peer_unit-1': 'peer_unit1_admin_addr',
'peer_unit-2': 'peer_unit2_admin_addr',
'this_unit-1': 'this_unit_admin_addr'},
'network': 'this_unit_admin_addr/admin_netmask'},
'this_unit_internal_addr': {
'backends': {
'peer_unit-1': 'peer_unit1_internal_addr',
'peer_unit-2': 'peer_unit2_internal_addr',
'this_unit-1': 'this_unit_internal_addr'},
'network': 'this_unit_internal_addr/internal_netmask'},
'this_unit_public_addr': {
'backends': {
'peer_unit-1': 'peer_unit1_public_addr',
'peer_unit-2': 'peer_unit2_public_addr',
'this_unit-1': 'this_unit_public_addr'},
'network': 'this_unit_public_addr/public_netmask'},
'this_unit_private_addr': {
'backends': {
'peer_unit-1': 'peer_unit1_private_addr',
'peer_unit-2': 'peer_unit2_private_addr',
'this_unit-1': 'this_unit_private_addr'},
'network': 'this_unit_private_addr/private_netmask'}}
expect_local_ns = copy.deepcopy(expect_full)
# Remove remote units from map of local unit and networks
for net in expect_full.keys():
for unit in expect_full[net]['backends'].keys():
if 'peer' in unit:
del expect_local_ns[net]['backends'][unit]
expect_local_default = {
'this_unit_private_addr': expect_local_ns['this_unit_private_addr']
}
del expect_local_ns['this_unit_private_addr']
# Tests PeerHARelationAdapter with peers
with mock.patch.object(adapters.ch_ip, 'get_address_in_network',
new=lambda x: test_addresses.get(x)), \
mock.patch.object(adapters.ch_ip, 'get_netmask_for_address',
new=lambda x: test_netmasks.get(x)), \
mock.patch.object(adapters, 'APIConfigurationAdapter',
side_effect=FakeAPIConfigAdapter), \
mock.patch.object(adapters.hookenv, 'config',
new=lambda: test_config):
fake = FakePeerRelation()
padapt = adapters.PeerHARelationAdapter
peer_ra = padapt(fake)
self.assertEqual(peer_ra.cluster_hosts, expect_full)
lnetsplit = padapt().local_network_split_addresses()
self.assertEqual(lnetsplit, expect_local_ns)
ldefault = padapt().local_default_addresses()
self.assertEqual(ldefault, expect_local_default)
self.assertEqual(peer_ra.internal_addresses, [
'peer_unit1_internal_addr',
'peer_unit2_internal_addr',
'this_unit_internal_addr'])
# Tests PeerHARelationAdapter without peers
with mock.patch.object(adapters.ch_ip, 'get_address_in_network',
new=lambda x: test_addresses.get(x)), \
mock.patch.object(adapters.ch_ip, 'get_netmask_for_address',
new=lambda x: test_netmasks.get(x)), \
mock.patch.object(adapters, 'APIConfigurationAdapter',
side_effect=FakeAPIConfigAdapter), \
mock.patch.object(adapters.hookenv, 'config',
new=lambda: test_config):
# Test single_mode_map when a cluster relation is present
with mock.patch.object(adapters.hookenv, 'relation_ids',
new=lambda x: ['rid1']), \
mock.patch.object(adapters.hookenv, 'related_units',
new=lambda relid: []):
expect = {
'internal_addresses': ['this_unit_internal_addr'],
'cluster_hosts': expect_local_ns}
expect['cluster_hosts']['this_unit_private_addr'] = \
expect_local_default['this_unit_private_addr']
peer_ra = adapters.PeerHARelationAdapter(
relation_name='cluster')
self.assertEqual(peer_ra.single_mode_map, expect)
# Test single_mode_map when a cluster relation is not present
# i.e is not defined in metadata.yaml
with mock.patch.object(adapters.hookenv, 'relation_ids',
new=lambda x: []):
peer_ra = adapters.PeerHARelationAdapter(FakePeerRelation())
self.assertEqual(peer_ra.single_mode_map, {})
class FakeDatabaseRelation():
auto_accessors = []
relation_name = 'shared_db'
def db_host(self):
return 'host1'
def username(self, prefix=''):
return 'username1{}'.format(prefix)
def password(self, prefix=''):
return 'password1{}'.format(prefix)
def database(self, prefix=''):
return 'database1{}'.format(prefix)
class SSLDatabaseRelationAdapter(adapters.DatabaseRelationAdapter):
ssl_ca = 'my-ca'
ssl_cert = 'my-cert'
ssl_key = 'my-key'
class TestDatabaseRelationAdapter(unittest.TestCase):
def test_class(self):
fake = FakeDatabaseRelation()
db = adapters.DatabaseRelationAdapter(fake)
self.assertEqual(db.host, 'host1')
self.assertEqual(db.type, 'mysql')
self.assertEqual(db.password, 'password1')
self.assertEqual(db.username, 'username1')
self.assertEqual(db.database, 'database1')
self.assertEqual(db.uri, 'mysql://username1:password1@host1/database1')
self.assertEqual(db.get_uri('x'),
'mysql://username1x:password1x@host1/database1x')
# test the ssl feature of the base class
db = SSLDatabaseRelationAdapter(fake)
self.assertEqual(db.uri,
'mysql://username1:password1@host1/database1'
'?ssl_ca=my-ca'
'&ssl_cert=my-cert&ssl_key=my-key')
class TestConfigurationAdapter(unittest.TestCase):
def test_class(self):
test_config = {
'one': 1,
'two': 2,
'three': 3,
'that-one': 4
}
with mock.patch.object(adapters.hookenv, 'config',
new=lambda: test_config):
c = adapters.ConfigurationAdapter()
self.assertEqual(c.one, 1)
self.assertEqual(c.three, 3)
self.assertEqual(c.that_one, 4)
def test_make_default_configuration_adapter_class(self):
# test that emply class just gives us a normal ConfigurationAdapter
self.assertEqual(
adapters.make_default_configuration_adapter_class(None, {}),
adapters.ConfigurationAdapter)
# now test with a custom class, but no properties
self.assertEqual(
adapters.make_default_configuration_adapter_class(
adapters.APIConfigurationAdapter, {}),
adapters.APIConfigurationAdapter)
# finally give it a custom property
def custom_property(config):
return 'custom-thing'
kls = adapters.make_default_configuration_adapter_class(
None, {'custom_property': custom_property})
self.assertEqual(kls.__name__, 'DefaultConfigurationAdapter')
self.assertTrue(
'ConfigurationAdapter' in [c.__name__ for c in kls.mro()])
# instantiate the kls and check for the property
test_config = {
'my-value': True,
}
with mock.patch.object(adapters.hookenv,
'config',
new=lambda: test_config):
c = kls()
self.assertTrue(c.my_value)
self.assertEqual(c.custom_property, 'custom-thing')
def test_charm_instance(self):
with mock.patch.object(adapters.hookenv, 'config', new=lambda: {}):
c = adapters.ConfigurationAdapter()
self.assertEqual(c.charm_instance, None)
class MockCharm(object):
pass
instance = MockCharm()
c = adapters.ConfigurationAdapter(charm_instance=instance)
self.assertEqual(c.charm_instance, instance)
self.assertTrue(c._charm_instance_weakref is not None)
class TestAPIConfigurationAdapter(unittest.TestCase):
api_ports = {
'svc1': {
'admin': 9001,
'public': 9001,
'internal': 9001,
},
'svc2': {
'admin': 9002,
'public': 9003,
'internal': 9002,
}}
def test_class(self):
test_config = {
'prefer-ipv6': False,
'vip': '',
}
with mock.patch.object(adapters.hookenv, 'config',
new=lambda: test_config), \
mock.patch.object(adapters.APIConfigurationAdapter,
'get_network_addresses'), \
mock.patch.object(adapters.hookenv, 'local_unit',
return_value='my-unit/0'):
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.local_unit_name, 'my-unit-0')
self.assertEqual(c.haproxy_stat_port, '8888')
self.assertEqual(c.service_ports, {})
self.assertEqual(c.service_listen_info, {})
self.assertEqual(c.external_endpoints, {})
def test_class_init_using_charm_instance(self):
class TestCharm(object):
api_ports = TestAPIConfigurationAdapter.api_ports
name = 'test-charm'
with mock.patch.object(adapters.hookenv, 'config', new=lambda: {}), \
mock.patch.object(adapters.APIConfigurationAdapter,
'get_network_addresses'):
c = adapters.APIConfigurationAdapter(charm_instance=TestCharm())
self.assertEqual(c.port_map, TestCharm.api_ports)
self.assertEqual(c.service_name, 'test-charm')
def test_ipv4_mode(self):
test_config = {
'prefer-ipv6': False,
'vip': '',
}
with mock.patch.object(adapters.ch_utils, 'get_host_ip',
return_value='10.0.0.10'), \
mock.patch.object(adapters.hookenv, 'config',
new=lambda: test_config), \
mock.patch.object(adapters.hookenv, 'unit_get',
return_value='10.0.0.20'), \
mock.patch.object(adapters.APIConfigurationAdapter,
'get_network_addresses'):
c = adapters.APIConfigurationAdapter(service_name='svc1')
self.assertFalse(c.ipv6_mode)
self.assertEqual(c.local_address, '10.0.0.10')
self.assertEqual(c.local_host, '127.0.0.1')
self.assertEqual(c.haproxy_host, '0.0.0.0')
self.assertEqual(c.service_name, 'svc1')
def test_external_endpoints(self):
with mock.patch.object(adapters.os_ip, 'resolve_address',
return_value="10.0.0.10"):
c = adapters.APIConfigurationAdapter(port_map=self.api_ports)
self.assertEqual(
c.external_endpoints, {
'svc1': {
'proto': 'https',
'ip': '10.0.0.10',
'port': 9001,
'url': 'https://10.0.0.10:9001'},
'svc2': {
'proto': 'https',
'ip': '10.0.0.10',
'port': 9002,
'url': 'https://10.0.0.10:9002'}})
def test_ipv6_mode(self):
test_config = {
'prefer-ipv6': True,
'vip': '',
}
with mock.patch.object(adapters.hookenv, 'config',
new=lambda: test_config), \
mock.patch.object(
adapters.ch_ip,
'get_ipv6_addr',
return_value=['fe80::f2de:f1ff:fedd:8dc7']), \
mock.patch.object(adapters.APIConfigurationAdapter,
'get_network_addresses'):
c = adapters.APIConfigurationAdapter()
self.assertTrue(c.ipv6_mode)
self.assertEqual(c.local_address, 'fe80::f2de:f1ff:fedd:8dc7')
self.assertEqual(c.local_host, 'ip6-localhost')
self.assertEqual(c.haproxy_host, '::')
def test_ipv6_enabled(self):
with mock.patch.object(adapters.ch_ip,
'is_ipv6_disabled') as is_ipv6_disabled:
# IPv6 disabled
is_ipv6_disabled.return_value = True
a = adapters.APIConfigurationAdapter()
self.assertEqual(a.ipv6_enabled, False)
# IPv6 enabled
is_ipv6_disabled.return_value = False
b = adapters.APIConfigurationAdapter()
self.assertEqual(b.ipv6_enabled, True)
def test_external_ports(self):
c = adapters.APIConfigurationAdapter(port_map=self.api_ports)
self.assertEqual(c.external_ports, {9001, 9002, 9003})
def test_get_network_addresses(self):
test_config = {
'prefer-ipv6': False,
'os-admin-network': 'admin_net',
'os-public-network': 'public_net',
'os-internal-network': 'internal_net',
}
test_networks = {
'admin_net': 'admin_addr',
'public_net': 'public_addr',
'internal_net': 'internal_addr',
}
resolved_addresses = {
'admin': 'admin_addr',
'public': 'public_addr',
'int': 'int_vip',
}
def _is_address_in_network(cidr, vip):
return cidr == vip.replace('vip_', '')
def _resolve_address(endpoint_type=None):
return resolved_addresses[endpoint_type]
with mock.patch.object(adapters.hookenv, 'config',
new=lambda: test_config), \
mock.patch.object(adapters.hookenv, 'unit_get',
return_value='thisunit'), \
mock.patch.object(adapters.os_ip, 'resolve_address',
new=_resolve_address), \
mock.patch.object(adapters.ch_ip, 'get_address_in_network',
new=lambda x, y: test_networks[x]):
c = adapters.APIConfigurationAdapter()
self.assertEqual(
c.get_network_addresses(), [
('admin_addr', 'admin_addr'),
('internal_addr', 'int_vip'),
('public_addr', 'public_addr')])
def test_port_maps(self):
class MockAddrAPIConfigurationAdapt(adapters.APIConfigurationAdapter):
@property
def local_address(self):
return '10.0.0.10'
def _determine_apache_port(port, singlenode_mode=None):
return port - 10
with mock.patch.object(adapters.ch_cluster, 'determine_apache_port',
side_effect=_determine_apache_port), \
mock.patch.object(adapters.APIConfigurationAdapter,
'determine_service_port',
side_effect=_determine_apache_port), \
mock.patch.object(adapters.os_ip, 'resolve_address',
return_value="10.10.10.10"):
with mock.patch.object(adapters.APIConfigurationAdapter,
'apache_enabled',
new=False):
c = MockAddrAPIConfigurationAdapt(port_map=self.api_ports)
self.assertEqual(
c.service_ports,
{'svc1_admin': [9001, 8991],
'svc2_admin': [9002, 8992],
'svc2_public': [9003, 8993]})
self.assertEqual(
c.service_listen_info, {
'svc1': {
'public_port': 8991,
'admin_port': 8991,
'internal_port': 8991,
'proto': 'http',
'ip': '10.0.0.10',
'port': 8991,
'url': 'http://10.0.0.10:8991'},
'svc2': {
'public_port': 8993,
'admin_port': 8992,
'internal_port': 8992,
'proto': 'http',
'ip': '10.0.0.10',
'port': 8992,
'url': 'http://10.0.0.10:8992'}})
self.assertEqual(
c.external_endpoints, {
'svc1': {
'proto': 'http',
'ip': '10.10.10.10',
'port': 9001,
'url': 'http://10.10.10.10:9001'},
'svc2': {
'proto': 'http',
'ip': '10.10.10.10',
'port': 9002,
'url': 'http://10.10.10.10:9002'}})
with mock.patch.object(adapters.APIConfigurationAdapter,
'apache_enabled',
new=True):
c = MockAddrAPIConfigurationAdapt(port_map=self.api_ports)
self.assertEqual(
c.service_ports,
{'svc1_admin': [9001, 8991],
'svc2_admin': [9002, 8992],
'svc2_public': [9003, 8993]})
self.assertEqual(
c.service_listen_info, {
'svc1': {
'public_port': 8991,
'admin_port': 8991,
'internal_port': 8991,
'proto': 'http',
'ip': '127.0.0.1',
'port': 8991,
'url': 'http://127.0.0.1:8991'},
'svc2': {
'public_port': 8993,
'admin_port': 8992,
'internal_port': 8992,
'proto': 'http',
'ip': '127.0.0.1',
'port': 8992,
'url': 'http://127.0.0.1:8992'}})
self.assertEqual(
c.external_endpoints, {
'svc1': {
'proto': 'https',
'ip': '10.10.10.10',
'port': 9001,
'url': 'https://10.10.10.10:9001'},
'svc2': {
'proto': 'https',
'ip': '10.10.10.10',
'port': 9002,
'url': 'https://10.10.10.10:9002'}})
def test_endpoints_and_ext_ports(self):
_net_addrs = [
('admin_addr', 'vip_admin_net'),
('internal_addr', 'vip_internal_net')]
with mock.patch.object(adapters.APIConfigurationAdapter,
'get_network_addresses',
return_value=_net_addrs), \
mock.patch.object(adapters.ch_cluster, 'determine_apache_port',
new=lambda x, singlenode_mode: x - 10), \
mock.patch.object(adapters.ch_cluster, 'determine_api_port',
new=lambda x, singlenode_mode: x - 20):
c = adapters.APIConfigurationAdapter(port_map=self.api_ports)
expect = [
('admin_addr', 'vip_admin_net', 8991, 8981),
('admin_addr', 'vip_admin_net', 8992, 8982),
('admin_addr', 'vip_admin_net', 8993, 8983),
('internal_addr', 'vip_internal_net', 8991, 8981),
('internal_addr', 'vip_internal_net', 8992, 8982),
('internal_addr', 'vip_internal_net', 8993, 8983),
]
self.assertEqual(c.endpoints, expect)
self.assertEqual(c.ext_ports, [8991, 8992, 8993])
def test_apache_enabled(self):
with mock.patch.object(adapters.charms.reactive.bus,
'get_state',
return_value=True):
c = adapters.APIConfigurationAdapter()
self.assertTrue(c.apache_enabled)
with mock.patch.object(adapters.charms.reactive.bus,
'get_state',
return_value=False):
c = adapters.APIConfigurationAdapter()
self.assertFalse(c.apache_enabled)
def test_determine_service_port(self):
with mock.patch.object(adapters.APIConfigurationAdapter,
'apache_enabled',
new=True):
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.determine_service_port(80), 60)
with mock.patch.object(adapters.APIConfigurationAdapter,
'apache_enabled',
new=False):
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.determine_service_port(80), 70)
def test_memcache_ctx(self):
class MockCharmInstance(object):
api_ports = {}
name = 'hello'
def __init__(self, release):
self.release = release
mch_result = False
class MCHR(object):
class_thing = None
def __init__(self, thing):
self.thing = thing
self.__class__.class_thing = self
def __gt__(self, other):
return mch_result
mpo = mock.patch.object
with mpo(adapters.ch_host, 'lsb_release') as lsb_r, \
mpo(adapters.ch_host, 'CompareHostReleases', new=MCHR), \
mpo(adapters.ch_ip, 'is_ipv6_disabled') as is_ipv6_disabled:
# first no memcache
mci = MockCharmInstance('liberty')
c = adapters.APIConfigurationAdapter(charm_instance=mci)
self.assertEqual(c.memcache['use_memcache'], False)
# next switch on memcache
mci = MockCharmInstance('mitaka')
# start with ipv6 disabled and ubuntu release is trusty
lsb_r.return_value = {'DISTRIB_CODENAME': 'trusty'}
is_ipv6_disabled.return_value = True
c = adapters.APIConfigurationAdapter(charm_instance=mci)
self.assertEqual(c.memcache['use_memcache'], True)
self.assertEqual(MCHR.class_thing.thing, 'trusty')
self.assertEqual(c.memcache['memcache_server'], 'localhost')
self.assertEqual(c.memcache['memcache_server_formatted'],
'127.0.0.1')
self.assertEqual(c.memcache['memcache_port'], '11211')
self.assertEqual(c.memcache['memcache_url'],
'127.0.0.1:11211')
# make us later than trusty
mch_result = True
self.assertEqual(c.memcache['memcache_server'], '127.0.0.1')
# now do ipv6 not disabled.
mch_result = False
is_ipv6_disabled.return_value = False
c = adapters.APIConfigurationAdapter(charm_instance=mci)
self.assertEqual(c.memcache['use_memcache'], True)
self.assertEqual(MCHR.class_thing.thing, 'trusty')
self.assertEqual(c.memcache['memcache_server'], 'ip6-localhost')
self.assertEqual(c.memcache['memcache_server_formatted'], '[::1]')
self.assertEqual(c.memcache['memcache_port'], '11211')
self.assertEqual(c.memcache['memcache_url'], 'inet6:[::1]:11211')
# make us later than trusty
mch_result = True
self.assertEqual(c.memcache['memcache_server'], '::1')
def test_use_memcache(self):
with mock.patch.object(adapters.APIConfigurationAdapter, 'memcache',
new_callable=mock.PropertyMock) as memcache:
memcache.return_value = {}
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.use_memcache, False)
memcache.return_value = {'use_memcache': False}
self.assertEqual(c.use_memcache, False)
memcache.return_value = {'use_memcache': True}
self.assertEqual(c.use_memcache, True)
def test_memcache_server(self):
with mock.patch.object(adapters.APIConfigurationAdapter, 'memcache',
new_callable=mock.PropertyMock) as memcache:
memcache.return_value = {}
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.memcache_server, '')
memcache.return_value = {'memcache_server': 'hello'}
self.assertEqual(c.memcache_server, 'hello')
def test_memcache_host(self):
with mock.patch.object(adapters.APIConfigurationAdapter, 'memcache',
new_callable=mock.PropertyMock) as memcache:
memcache.return_value = {}
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.memcache_host, '')
memcache.return_value = {'memcache_server_formatted': 'hello'}
self.assertEqual(c.memcache_host, 'hello')
def test_memcache_port(self):
with mock.patch.object(adapters.APIConfigurationAdapter, 'memcache',
new_callable=mock.PropertyMock) as memcache:
memcache.return_value = {}
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.memcache_port, '')
memcache.return_value = {'memcache_port': 'hello'}
self.assertEqual(c.memcache_port, 'hello')
def test_memcache_url(self):
with mock.patch.object(adapters.APIConfigurationAdapter, 'memcache',
new_callable=mock.PropertyMock) as memcache:
memcache.return_value = {}
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.memcache_url, '')
memcache.return_value = {'memcache_url': 'hello'}
self.assertEqual(c.memcache_url, 'hello')
def test_workers(self):
class FakeWorkerConfigContext(object):
def __call__(self):
return {"workers": 8}
with mock.patch.object(adapters.ch_context, 'WorkerConfigContext',
new=FakeWorkerConfigContext):
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.workers, 8)
def test_wsgi_worker_context(self):
class ChInstance1(object):
name = 'test-name'
wsgi_script = 'test-script'
api_ports = {}
class ChInstance2(object):
name = 'test-name'
wsgi_script = 'test-script'
wsgi_admin_script = 'test-admin-script'
wsgi_public_script = 'test-public-script'
wsgi_process_weight = 0.5
wsgi_admin_process_weight = 0.1
wsgi_public_process_weight = 0.4
api_ports = {}
class ChInstance3(object):
name = 'test-name'
wsgi_script = None
wsgi_admin_script = 'test-admin-script'
wsgi_public_script = 'test-public-script'
wsgi_process_weight = None
wsgi_admin_process_weight = 0.1
wsgi_public_process_weight = 0.4
api_ports = {}
class FakeWSGIWorkerConfigContext():
copy_kwargs = None
def __init__(self, **kwargs):
self.__class__.copy_kwargs = kwargs.copy()
def __call__(self):
return "T"
with mock.patch.object(adapters.ch_context, 'WSGIWorkerConfigContext',
new=FakeWSGIWorkerConfigContext):
# start with no charm instance to get default values
c = adapters.APIConfigurationAdapter()
self.assertEqual(c.wsgi_worker_context, "T")
self.assertEqual(FakeWSGIWorkerConfigContext.copy_kwargs, {})
# start with a minimal charm_instance
instance = ChInstance1()
c = adapters.APIConfigurationAdapter(charm_instance=instance)
self.assertEqual(c.wsgi_worker_context, "T")
self.assertEqual(FakeWSGIWorkerConfigContext.copy_kwargs,
{'name': 'test-name', 'script': 'test-script'})
# And then, all the options set:
instance = ChInstance2()
c = adapters.APIConfigurationAdapter(charm_instance=instance)
self.assertEqual(c.wsgi_worker_context, "T")
self.assertEqual(FakeWSGIWorkerConfigContext.copy_kwargs,
{'name': 'test-name',
'script': 'test-script',
'admin_script': 'test-admin-script',
'public_script': 'test-public-script',
'process_weight': 0.5,
'admin_process_weight': 0.1,
'public_process_weight': 0.4})
# and finally, with some of the options set to None, to test
# filtering
instance = ChInstance3()
c = adapters.APIConfigurationAdapter(charm_instance=instance)
self.assertEqual(c.wsgi_worker_context, "T")
self.assertEqual(FakeWSGIWorkerConfigContext.copy_kwargs,
{'name': 'test-name',
'admin_script': 'test-admin-script',
'public_script': 'test-public-script',
'admin_process_weight': 0.1,
'public_process_weight': 0.4})
class FakePeerHARelationAdapter(object):
def __init__(self, relation=None, relation_name=None):
self.hadict = {
'cluster_hosts': {'my': 'map'}}
def __getitem__(self, arg):
return self.hadict[arg]
@property
def single_mode_map(self):
return self.hadict
class FakePeerHARelationAdapter2(FakePeerHARelationAdapter):
@property
def single_mode_map(self):
return None
class TestOpenStackRelationAdapters(unittest.TestCase):
# test the OpenStackRelationAdapters() class, and then derive from it to
# test the additonal relation_adapters member on __init__
def test_class(self):
test_config = {
'one': 1,
'two': 2,
'three': 3,
'that-one': 4
}
with mock.patch.object(adapters, 'PeerHARelationAdapter',
new=FakePeerHARelationAdapter), \
mock.patch.object(adapters.hookenv, 'config',
new=lambda: test_config):
amqp = FakeRabbitMQRelation()
shared_db = FakeDatabaseRelation()
mine = MyRelation()
a = adapters.OpenStackRelationAdapters([amqp, shared_db, mine])
self.assertEqual(a.amqp.private_address, 'private-address')
self.assertEqual(a.my_name.this, 'this')
# pick the keys off the __iter__() for the adapters instance
items = [x[0] for x in list(a)]
self.assertTrue('options' in items)
# self.assertTrue('cluster' in items)
self.assertTrue('amqp' in items)
self.assertTrue('shared_db' in items)
self.assertTrue('my_name' in items)
def test_set_charm_instance(self):
# a fake charm instance to play with
class FakeCharm(object):
name = 'fake-charm'
shared_db = FakeDatabaseRelation()
charm = FakeCharm()
a = adapters.OpenStackRelationAdapters([shared_db],
charm_instance=charm)
self.assertEqual(a.charm_instance, charm)
def test_custom_configurations_creation(self):
# Test we can bring in a custom configurations
class FakeConfigurationAdapter(adapters.ConfigurationAdapter):
def __init__(self, charm_instance):
self.test = 'hello'
class FakeCharm(object):
name = 'fake-charm'
configuration_class = FakeConfigurationAdapter
with mock.patch.object(adapters, '_custom_config_properties', new={}):
@adapters.config_property
def custom_prop(config):
return config.test
a = adapters.OpenStackRelationAdapters(
[], charm_instance=FakeCharm())
self.assertEqual(a.options.custom_prop, 'hello')
self.assertIsInstance(a.options, FakeConfigurationAdapter)
def test_hoists_custom_relation_properties(self):
class FakeConfigurationAdapter(adapters.ConfigurationAdapter):
def __init__(self, charm_instance):
pass
class FakeSharedDBAdapter(adapters.OpenStackRelationAdapter):
interface_name = 'shared-db'
class FakeThingAdapter(adapters.OpenStackRelationAdapter):
interface_name = 'some-interface'
class FakeAdapters(adapters.OpenStackRelationAdapters):
# override the relation_adapters to our shared_db adapter
relation_adapters = {
'shared-db': FakeSharedDBAdapter,
'some-interface': FakeThingAdapter,
}
class FakeThing(object):
relation_name = 'some-interface'
auto_accessors = []
class FakeSharedDB(object):
relation_name = 'shared-db'
auto_accessors = ('thing',)
def thing(self):
return 'kenobi'
class FakeCharm(object):
name = 'fake-charm'
adapters_class = FakeAdapters
configuration_class = FakeConfigurationAdapter
with mock.patch.object(adapters, '_custom_adapter_properties', {}):
@adapters.adapter_property('some-interface')
def custom_property(interface):
return 'goodbye'
@adapters.adapter_property('shared-db')
def custom_thing(shared_db):
return 'obe wan {}'.format(shared_db.thing)
shared_db = FakeSharedDB()
fake_thing = FakeThing()
a = FakeAdapters([shared_db, fake_thing],
charm_instance=FakeCharm())
# Verify that the custom properties got set.
# This also checks that all the classes were instantiated
self.assertEqual(a.some_interface.custom_property, 'goodbye')
self.assertEqual(a.shared_db.custom_thing, 'obe wan kenobi')
# verify that the right relations clases were instantiated.
# Note that this checks that the adapters' inheritence is correct;
# they are actually modified classes.
self.assertEqual(len(a._adapters), 2)
self.assertIsInstance(a.some_interface, FakeThingAdapter)
self.assertNotEqual(a.some_interface.__class__.__name__,
'FakeThingAdapter')
self.assertIsInstance(a.shared_db, FakeSharedDBAdapter)
self.assertNotEqual(a.shared_db.__class__.__name__,
'FakeSharedDBAdapter')
# verify that the iteration of the adapters yields the interfaces
ctxt = dict(a)
self.assertIsInstance(ctxt['options'], FakeConfigurationAdapter)
self.assertIsInstance(ctxt['shared_db'], FakeSharedDBAdapter)
self.assertIsInstance(ctxt['some_interface'], FakeThingAdapter)
self.assertEqual(len(ctxt.keys()), 3)
class MyRelationAdapter(adapters.OpenStackRelationAdapter):
@property
def us(self):
return self.this + '-us'
class MyOpenStackRelationAdapters(adapters.OpenStackRelationAdapters):
relation_adapters = {
'my_name': MyRelationAdapter,
}
class MyOpenStackAPIRelationAdapters(adapters.OpenStackAPIRelationAdapters):
relation_adapters = {
'my_name': MyRelationAdapter,
}
class MyConfigAdapter(adapters.ConfigurationAdapter):
def __init__(self, key1=None):
self.customarg = key1
self.instancearg = 'instancearg1'
class TestCustomOpenStackRelationAdapters(unittest.TestCase):
def test_class(self):
test_config = {
'one': 1,
'two': 2,
'three': 3,
'that-one': 4
}
with mock.patch.object(adapters.hookenv, 'related_units',
return_value=[]), \
mock.patch.object(adapters.hookenv,
'config',
new=lambda: test_config):
amqp = FakeRabbitMQRelation()
shared_db = FakeDatabaseRelation()
mine = MyRelation()
# Test using deprecated 'options' argument to pass in
# configuration class
a = MyOpenStackRelationAdapters([amqp, shared_db, mine],
options=MyConfigAdapter,)
self.assertEqual(a.my_name.us, 'this-us')
self.assertEqual(a.options.instancearg, 'instancearg1')
# Test using 'options_instance' argument to pass in
# instance of configuration class
b = MyOpenStackRelationAdapters(
[amqp, shared_db, mine],
options_instance=MyConfigAdapter(key1='customarg1'),)
self.assertEqual(b.my_name.us, 'this-us')
self.assertEqual(b.options.instancearg, 'instancearg1')
self.assertEqual(b.options.customarg, 'customarg1')
class TestCustomOpenStackAPIRelationAdapters(unittest.TestCase):
def test_class(self):
test_config = {
'one': 1,
'two': 2,
'three': 3,
'that-one': 4
}
with mock.patch.object(adapters.hookenv, 'related_units',
return_value=[]), \
mock.patch.object(adapters.hookenv,
'config',
new=lambda: test_config), \
mock.patch.object(adapters, 'PeerHARelationAdapter',
new=FakePeerHARelationAdapter):
amqp = FakeRabbitMQRelation()
shared_db = FakeDatabaseRelation()
mine = MyRelation()
# Test using deprecated 'options' argument to pass in
# configuration class
a = MyOpenStackAPIRelationAdapters([amqp, shared_db, mine],
options=MyConfigAdapter,)
self.assertEqual(a.my_name.us, 'this-us')
self.assertEqual(a.options.instancearg, 'instancearg1')
self.assertEqual(a.cluster['cluster_hosts'], {'my': 'map'})
# Test using 'options_instance' argument to pass in
# instance of configuration class
b = MyOpenStackAPIRelationAdapters(
[amqp, shared_db, mine],
options_instance=MyConfigAdapter(key1='customarg1'),)
self.assertEqual(b.my_name.us, 'this-us')
self.assertEqual(b.options.instancearg, 'instancearg1')
self.assertEqual(b.options.customarg, 'customarg1')
self.assertEqual(b.cluster['cluster_hosts'], {'my': 'map'})
def test_add_cluster(self):
test_config = {
'one': 1,
'two': 2,
'three': 3,
'that-one': 4
}
with mock.patch.object(adapters.hookenv, 'related_units',
return_value=[]), \
mock.patch.object(adapters.hookenv,
'config',
new=lambda: test_config), \
mock.patch.object(adapters.reactive.RelationBase,
'from_state',
new=FakePeerHARelationAdapter), \
mock.patch.object(adapters, 'PeerHARelationAdapter',
new=FakePeerHARelationAdapter2):
b = MyOpenStackAPIRelationAdapters([])
self.assertEqual(b.cluster['cluster_hosts'], {'my': 'map'})