summaryrefslogtreecommitdiff
diff options
authorBilly Olsen <billy.olsen@canonical.com>2014-12-17 23:42:08 -0700
committerBilly Olsen <billy.olsen@canonical.com>2014-12-17 23:42:08 -0700
commitcbf28963824fc2867182e6711cc4bae8df01cdd9 (patch)
tree81f030a70e221ff54b160fb0c69e40970811fa24
parent94e8c29812dd2086ce4a028945e6a316e3546c4d (diff)
Finish out unit test coverage for changes.
-rwxr-xr-xhooks/hooks.py36
-rw-r--r--unit_tests/test_hooks.py166
-rw-r--r--unit_tests/test_utils.py8
3 files changed, 188 insertions, 22 deletions
diff --git a/hooks/hooks.py b/hooks/hooks.py
index 84ffa9d..747d0ad 100755
--- a/hooks/hooks.py
+++ b/hooks/hooks.py
@@ -49,15 +49,16 @@ except ImportError:
pass
from charmhelpers.core.hookenv import (
+ close_port,
config,
+ local_unit,
+ open_port,
unit_get,
relation_get,
relation_set,
relations_of_type,
relation_id,
relation_ids,
- open_port,
- close_port,
Hooks,
DEBUG,
)
@@ -190,6 +191,10 @@ def process_check_pidfile(pidfile=None):
return((None, None))
+class MasterNotFoundException(Exception):
+ pass
+
+
class TimeoutException(Exception):
pass
@@ -384,13 +389,8 @@ def mongo_client_smart(host='localhost', command=None):
if command is None:
return(False)
- cmd_line = []
- cmd_line.append('mongo')
- cmd_line.append('--quiet')
- cmd_line.append('--host')
- cmd_line.append(host)
- cmd_line.append('--eval')
- cmd_line.append('printjson(%s)' % command)
+ cmd_line = ['mongo', '--quiet', '--host', host,
+ '--eval', 'printjson(%s)' % command]
juju_log("mongo_client_smart executing: %s" % str(cmd_line), DEBUG)
for i in xrange(0, 10):
@@ -1078,7 +1078,7 @@ def replica_set_relation_joined():
def get_unit_id():
- return os.environ.get('JUJU_UNIT_NAME').split('/')[1]
+ return local_unit().split('/')[1]
def get_replicaset_members():
@@ -1110,6 +1110,7 @@ def get_replicaset_master():
(idx, hostname, port) = member
if "%s:%s" % (hostname, port) == replica:
return member
+ raise MasterNotFoundException("%s was not found." % (replica))
return (current_id,
unit_get('private-address'),
@@ -1145,11 +1146,14 @@ def am_i_primary():
def replica_set_relation_changed():
(idx, master_hostname, master_port) = get_replicaset_master()
+ private_address = unit_get('private-address')
+ remote_hostname = relation_get('hostname')
+
juju_log('replica_set_relation_changed-start')
- juju_log('I am: %s' % unit_get('private-address'))
- juju_log('Joiner: %s' % relation_get('hostname'))
+ juju_log('I am: %s' % (private_address))
+ juju_log('Joiner: %s' % (remote_hostname))
- if relation_get('hostname') is None:
+ if remote_hostname is None:
juju_log('Joiner not ready yet... bailing out')
return
@@ -1168,10 +1172,8 @@ def replica_set_relation_changed():
(master_hostname, master_port), DEBUG)
init_replset("%s:%s" % (master_hostname, master_port))
- unit = "%s:%s" % (unit_get('private-address'),
- config('port'))
- unit_remote = "%s:%s" % (relation_get('hostname'),
- relation_get('port'))
+ unit = "%s:%s" % (private_address, config('port'))
+ unit_remote = "%s:%s" % (remote_hostname, relation_get('port'))
juju_log("DEBUG: %s - %s" % (unit, master_hostname))
# If this is primary, add joined unit to replicaset
diff --git a/unit_tests/test_hooks.py b/unit_tests/test_hooks.py
index 1aa2450..74bc163 100644
--- a/unit_tests/test_hooks.py
+++ b/unit_tests/test_hooks.py
@@ -1,4 +1,4 @@
-from mock import patch
+from mock import patch, call
import hooks
@@ -30,6 +30,7 @@ class MongoHooksTest(CharmTestCase):
# the hooks object will return the value that is set in the test case's
# test_config dictionary
self.config.side_effect = self.test_config.get
+ self.relation_get.side_effect = self.test_relation.get
@patch.object(hooks, 'restart_mongod')
@patch.object(hooks, 'enable_replset')
@@ -204,6 +205,7 @@ class MongoHooksTest(CharmTestCase):
mock_admin_cmd.side_effect = OperationFailure('unexpected failure')
try:
hooks.am_i_primary()
+ self.assertFalse(True, "Expected OperationFailure to be raised")
except OperationFailure:
mock_admin_cmd.assert_called()
@@ -265,3 +267,165 @@ class MongoHooksTest(CharmTestCase):
% ('fake-host', 'fake-command'))
mock_subprocess.assert_called_once_with(expected_cmd, shell=True)
self.assertFalse(rv)
+
+ @patch.object(hooks, 'local_unit')
+ def test_get_unit_id(self, mock_local_unit):
+ mock_local_unit.return_value = 'fake-mongo-unit/2'
+ self.assertEqual('2', hooks.get_unit_id())
+
+ @patch.object(hooks, 'relation_get')
+ @patch.object(hooks, 'relations_of_type')
+ def test_get_replicaset_members(self, mock_relations,
+ mock_relation_get):
+ mock_relations.return_value = [{'__unit__': 'mongodb/0'},
+ {'__unit__': 'mongodb/1'}]
+
+ config_stuff = {'mongodb/0': {'install-order': 1,
+ 'hostname': 'juju-local-unit-1',
+ 'port': '27817'},
+ 'mongodb/1': {'install-order': 0,
+ 'hostname': 'juju-local-unit-2',
+ 'port': '27817'}}
+
+ def side_effect(key, member):
+ member_info = config_stuff.get(member, None)
+ if member_info is None:
+ return None
+
+ return member_info.get(key, None)
+
+ mock_relation_get.side_effect = side_effect
+ members = hooks.get_replicaset_members()
+ self.assertEqual(2, len(members))
+
+ # Expect a re-ordering based on the install-order
+ exp_members = [(0, 'juju-local-unit-2', '27817'),
+ (1, 'juju-local-unit-1', '27817')]
+ self.assertEqual(members, exp_members)
+
+ @patch.object(hooks, 'get_replicaset_members')
+ @patch.object(hooks, 'get_unit_id')
+ @patch.object(hooks, 'unit_get')
+ def test_get_replicaset_master(self, mock_unit_get, mock_unit_id,
+ mock_members):
+ mock_unit_id.return_value = 0
+ mock_unit_get.return_value = 'juju-local-unit-1'
+
+ self.test_config.set('port', '12345')
+
+ # First check no members
+ mock_members.return_value = []
+ master = hooks.get_replicaset_master()
+ self.assertEqual(master, (0, 'juju-local-unit-1', '12345'))
+
+ # Check auto, curr unit is lowest unit
+ self.test_config.set('replicaset_master', 'auto')
+ mock_members.return_value = [(1, 'juju-local-unit-2', '12345'),
+ (2, 'juju-local-unit-3', '12345')]
+ master = hooks.get_replicaset_master()
+ self.assertEqual(master, (0, 'juju-local-unit-1', '12345'))
+
+ # Check auto, curr unit is NOT lowest unit
+ mock_unit_id.return_value = 3
+ master = hooks.get_replicaset_master()
+ self.assertEqual(master, (1, 'juju-local-unit-2', '12345'))
+
+ # Check not-auto, master found
+ self.test_config.set('replicaset_master', 'juju-local-unit-3:12345')
+ master = hooks.get_replicaset_master()
+ self.assertEqual(master, (2, 'juju-local-unit-3', '12345'))
+
+ # Check not-auto, master NOT found
+ self.test_config.set('replicaset_master', 'nonexistent-unit:7890')
+ try:
+ master = hooks.get_replicaset_master()
+ self.assertTrue(False, 'replicaset master specified should error')
+ except hooks.MasterNotFoundException:
+ pass
+
+ @patch.object(hooks, 'am_i_primary')
+ @patch.object(hooks, 'init_replset')
+ @patch.object(hooks, 'relation_get')
+ @patch.object(hooks, 'peer_units')
+ @patch.object(hooks, 'oldest_peer')
+ @patch.object(hooks, 'join_replset')
+ @patch.object(hooks, 'unit_get')
+ @patch.object(hooks, 'get_replicaset_master')
+ def test_replica_set_relation_changed(self, mock_master, mock_unit_get,
+ mock_join_replset, mock_oldest_peer,
+ mock_peer_units, mock_relation_get,
+ mock_init_replset, mock_is_primary):
+ # set the unit_get('private-address')
+ mock_unit_get.return_value = 'juju-local-unit-0.local'
+ mock_master.return_value = (0, 'juju-local-unit-0', '12345')
+ mock_relation_get.return_value = None
+
+ # Test when remote hostname is None, should not join
+ hooks.replica_set_relation_changed()
+ self.assertEqual(0, mock_join_replset.call_count)
+
+ # Test remote hostname is valid, but master is somehow not defined
+ mock_join_replset.reset_mock()
+ mock_master.reset_mock()
+ mock_master.return_value = (0, None, None)
+ mock_relation_get.return_value = 'juju-local-unit-0'
+
+ hooks.replica_set_relation_changed()
+
+ self.assertEqual(0, mock_join_replset.call_count)
+
+ # Test when not oldest peer, don't init replica set
+ mock_peer_units.return_value = ['mongodb/1', 'mongodb/2']
+ mock_oldest_peer.return_value = False
+
+ hooks.replica_set_relation_changed()
+
+ self.assertEqual(mock_init_replset.call_count, 0)
+
+ # Test when this is the oldest peer
+ mock_master.reset_mock()
+ mock_master.return_value = (0, 'juju-unit-0', '12345')
+ mock_oldest_peer.reset_mock()
+ mock_oldest_peer.return_value = True
+ mock_is_primary.return_value = False
+
+ hooks.replica_set_relation_changed()
+ call1 = call('juju-unit-0:12345')
+
+ mock_init_replset.assert_has_calls(call1)
+
+ # Test when its also the PRIMARY
+ mock_relation_get.reset_mock()
+ mock_relation_get.side_effect = ['juju-remote-unit-0', '12345']
+ mock_oldest_peer.reset_mock()
+ mock_oldest_peer.return_value = False
+ mock_is_primary.reset_mock()
+ mock_is_primary.return_value = True
+
+ hooks.replica_set_relation_changed()
+ call1 = call('juju-local-unit-0.local:27017',
+ 'juju-remote-unit-0:12345')
+ mock_join_replset.assert_has_calls(call1)
+
+ @patch.object(hooks, 'unit_get')
+ @patch.object(hooks, 'leave_replset')
+ @patch.object(hooks, 'am_i_primary')
+ def test_replica_set_relation_departed(self, mock_am_i_primary,
+ mock_leave_replset, mock_unit_get):
+ mock_am_i_primary.return_value = False
+ hooks.replica_set_relation_departed()
+
+ self.assertEqual(0, mock_leave_replset.call_count)
+
+ mock_am_i_primary.reset_mock()
+ mock_am_i_primary.return_value = True
+ mock_unit_get.return_value = 'juju-local'
+
+ self.test_relation.set({'hostname': 'juju-remote',
+ 'port': '27017'})
+ mock_leave_replset.reset_mock()
+
+ hooks.replica_set_relation_departed()
+
+ call1 = call('juju-local:27017', 'juju-remote:27017')
+ mock_leave_replset.assert_has_calls(call1)
diff --git a/unit_tests/test_utils.py b/unit_tests/test_utils.py
index e90679e..1f2a1aa 100644
--- a/unit_tests/test_utils.py
+++ b/unit_tests/test_utils.py
@@ -91,9 +91,9 @@ class TestConfig(object):
return self.config
def set(self, attr, value):
- if attr not in self.config:
- raise KeyError
- self.config[attr] = value
+ if attr not in self.config:
+ raise KeyError
+ self.config[attr] = value
class TestRelation(object):
@@ -107,5 +107,5 @@ class TestRelation(object):
if attr is None:
return self.relation_data
elif attr in self.relation_data:
- return self.relation_data[attr]
+ return self.relation_data.get(attr)
return None