diff options
| author | Billy Olsen <billy.olsen@canonical.com> | 2014-12-17 23:42:08 -0700 | 
|---|---|---|
| committer | Billy Olsen <billy.olsen@canonical.com> | 2014-12-17 23:42:08 -0700 | 
| commit | cbf28963824fc2867182e6711cc4bae8df01cdd9 (patch) | |
| tree | 81f030a70e221ff54b160fb0c69e40970811fa24 | |
| parent | 94e8c29812dd2086ce4a028945e6a316e3546c4d (diff) | |
Finish out unit test coverage for changes.
| -rwxr-xr-x | hooks/hooks.py | 36 | ||||
| -rw-r--r-- | unit_tests/test_hooks.py | 166 | ||||
| -rw-r--r-- | unit_tests/test_utils.py | 8 | 
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 | 
