class TestEtcdWatcher(BaseTestCase): def setUp(self): super(TestEtcdWatcher, self).setUp() self.reconnect_patch = patch("calico.etcdutils.EtcdWatcher.reconnect") self.m_reconnect = self.reconnect_patch.start() self.watcher = EtcdWatcher(["foobar:4001"], "/calico") self.m_client = Mock() self.watcher.client = self.m_client self.m_dispatcher = Mock(spec=PathDispatcher) self.watcher.dispatcher = self.m_dispatcher @patch("time.sleep", autospec=True) def test_mainline(self, m_sleep): m_snap_response = Mock() m_snap_response.etcd_index = 1 m_poll_response = Mock() m_poll_response.modifiedIndex = 2 responses = [ m_snap_response, m_poll_response, ResyncRequired(), # Loop 1 EtcdException(), # Loop 2 ExpectedException(), # Loop 3, Break out of loop. ] self.m_client.read.side_effect = iter(responses) with patch.object(self.watcher, "_on_pre_resync", autospec=True) as m_pre_r: with patch.object(self.watcher, "_on_snapshot_loaded", autospec=True) as m_snap_load: self.assertRaises(ExpectedException, self.watcher.loop) # _on_pre_resync() called once per loop. self.assertEqual(m_pre_r.mock_calls, [call(), call(), call()]) # The snapshot only loads successfully the first time. self.assertEqual(m_snap_load.mock_calls, [call(m_snap_response)]) self.assertEqual(self.m_dispatcher.handle_event.mock_calls, [call(m_poll_response)]) # Should sleep after exception. m_sleep.assert_called_once_with(1) def test_loop_stopped(self): self.watcher._stopped = True with patch.object(self.watcher, "_on_pre_resync", autospec=True) as m_pre_r: self.watcher.loop() self.assertFalse(m_pre_r.called) def test_register(self): self.watcher.register_path("key", foo="bar") self.assertEqual(self.m_dispatcher.register.mock_calls, [call("key", foo="bar")]) @patch("time.sleep", autospec=True) def test_wait_for_ready(self, m_sleep): m_resp_1 = Mock() m_resp_1.value = "false" m_resp_2 = Mock() m_resp_2.value = "true" responses = [ etcd.EtcdException(), etcd.EtcdKeyNotFound(), m_resp_1, m_resp_2, ] self.m_client.read.side_effect = iter(responses) self.watcher.wait_for_ready(1) self.assertEqual(m_sleep.mock_calls, [call(1)] * 3) def test_load_initial_dump(self): m_response = Mock(spec=etcd.EtcdResult) m_response.etcd_index = 10000 self.m_client.read.side_effect = [ etcd.EtcdKeyNotFound(), m_response ] with patch("time.sleep") as m_sleep: self.assertEqual(self.watcher.load_initial_dump(), m_response) m_sleep.assert_called_once_with(1) self.m_client.read.assert_has_calls([ call("/calico", recursive=True), call("/calico", recursive=True), ]) self.assertEqual(self.watcher.next_etcd_index, 10001) def test_load_initial_dump_stopped(self): self.watcher.stop() self.m_client.read.side_effect = etcd.EtcdKeyNotFound() self.assertRaises(etcd.EtcdKeyNotFound, self.watcher.load_initial_dump) def test_resync_set(self): self.watcher.next_etcd_index = 1 self.watcher.resync_after_current_poll = True self.assertRaises(ResyncRequired, self.watcher.wait_for_etcd_event) self.assertFalse(self.watcher.resync_after_current_poll) @patch("time.sleep", autospec=True) def test_wait_for_etcd_event_conn_failed(self, m_sleep): self.watcher.next_etcd_index = 1 m_resp = Mock() m_resp.modifiedIndex = 123 read_timeout = etcd.EtcdConnectionFailed() read_timeout.cause = ReadTimeoutError(Mock(), "", "") other_error = etcd.EtcdConnectionFailed() other_error.cause = ExpectedException() responses = [ read_timeout, other_error, m_resp, ] self.m_client.read.side_effect = iter(responses) event = self.watcher.wait_for_etcd_event() self.assertEqual(event, m_resp) self.assertEqual(m_sleep.mock_calls, [call(1)]) def test_wait_for_etcd_event_cluster_id_changed(self): self.watcher.next_etcd_index = 1 responses = [ etcd.EtcdClusterIdChanged(), ] self.m_client.read.side_effect = iter(responses) self.assertRaises(ResyncRequired, self.watcher.wait_for_etcd_event) def test_wait_for_etcd_event_index_cleared(self): self.watcher.next_etcd_index = 1 responses = [ etcd.EtcdEventIndexCleared(), ] self.m_client.read.side_effect = iter(responses) self.assertRaises(ResyncRequired, self.watcher.wait_for_etcd_event) @patch("time.sleep", autospec=True) def test_wait_for_etcd_event_unexpected_error(self, m_sleep): self.watcher.next_etcd_index = 1 responses = [ etcd.EtcdException(), ] self.m_client.read.side_effect = iter(responses) self.assertRaises(ResyncRequired, self.watcher.wait_for_etcd_event) self.assertEqual(m_sleep.mock_calls, [call(1)]) def test_coverage(self): # These methods are no-ops. self.watcher._on_pre_resync() self.watcher._on_snapshot_loaded(Mock()) def tearDown(self): self.reconnect_patch.stop() super(TestEtcdWatcher, self).tearDown()
class TestEtcdWatcher(BaseTestCase): def setUp(self): super(TestEtcdWatcher, self).setUp() self.reconnect_patch = patch("calico.etcdutils.EtcdWatcher.reconnect") self.m_reconnect = self.reconnect_patch.start() self.watcher = EtcdWatcher("foobar:4001", "/calico") self.m_client = Mock() self.watcher.client = self.m_client self.m_dispatcher = Mock(spec=PathDispatcher) self.watcher.dispatcher = self.m_dispatcher @patch("time.sleep", autospec=True) def test_mainline(self, m_sleep): m_snap_response = Mock() m_snap_response.etcd_index = 1 m_poll_response = Mock() m_poll_response.modifiedIndex = 2 responses = [ m_snap_response, m_poll_response, ResyncRequired(), # Loop 1 EtcdException(), # Loop 2 ExpectedException(), # Loop 3, Break out of loop. ] self.m_client.read.side_effect = iter(responses) with patch.object(self.watcher, "_on_pre_resync", autospec=True) as m_pre_r: with patch.object(self.watcher, "_on_snapshot_loaded", autospec=True) as m_snap_load: self.assertRaises(ExpectedException, self.watcher.loop) # _on_pre_resync() called once per loop. self.assertEqual(m_pre_r.mock_calls, [call(), call(), call()]) # The snapshot only loads successfully the first time. self.assertEqual(m_snap_load.mock_calls, [call(m_snap_response)]) self.assertEqual(self.m_dispatcher.handle_event.mock_calls, [call(m_poll_response)]) # Should sleep after exception. m_sleep.assert_called_once_with(1) def test_loop_stopped(self): self.watcher._stopped = True with patch.object(self.watcher, "_on_pre_resync", autospec=True) as m_pre_r: self.watcher.loop() self.assertFalse(m_pre_r.called) def test_register(self): self.watcher.register_path("key", foo="bar") self.assertEqual(self.m_dispatcher.register.mock_calls, [call("key", foo="bar")]) @patch("time.sleep", autospec=True) def test_wait_for_ready(self, m_sleep): m_resp_1 = Mock() m_resp_1.value = "false" m_resp_2 = Mock() m_resp_2.value = "true" responses = [ etcd.EtcdException(), etcd.EtcdKeyNotFound(), m_resp_1, m_resp_2, ] self.m_client.read.side_effect = iter(responses) self.watcher.wait_for_ready(1) self.assertEqual(m_sleep.mock_calls, [call(1)] * 3) def test_load_initial_dump(self): m_response = Mock(spec=etcd.EtcdResult) m_response.etcd_index = 10000 self.m_client.read.side_effect = [ etcd.EtcdKeyNotFound(), m_response ] with patch("time.sleep") as m_sleep: self.assertEqual(self.watcher.load_initial_dump(), m_response) m_sleep.assert_called_once_with(1) self.m_client.read.assert_has_calls([ call("/calico", recursive=True), call("/calico", recursive=True), ]) self.assertEqual(self.watcher.next_etcd_index, 10001) def test_load_initial_dump_stopped(self): self.watcher.stop() self.m_client.read.side_effect = etcd.EtcdKeyNotFound() self.assertRaises(etcd.EtcdKeyNotFound, self.watcher.load_initial_dump) def test_resync_set(self): self.watcher.next_etcd_index = 1 self.watcher.resync_after_current_poll = True self.assertRaises(ResyncRequired, self.watcher.wait_for_etcd_event) self.assertFalse(self.watcher.resync_after_current_poll) @patch("time.sleep", autospec=True) def test_wait_for_etcd_event_conn_failed(self, m_sleep): self.watcher.next_etcd_index = 1 m_resp = Mock() m_resp.modifiedIndex = 123 read_timeout = etcd.EtcdConnectionFailed() read_timeout.cause = ReadTimeoutError(Mock(), "", "") other_error = etcd.EtcdConnectionFailed() other_error.cause = ExpectedException() responses = [ read_timeout, other_error, m_resp, ] self.m_client.read.side_effect = iter(responses) event = self.watcher.wait_for_etcd_event() self.assertEqual(event, m_resp) self.assertEqual(m_sleep.mock_calls, [call(1)]) def test_wait_for_etcd_event_cluster_id_changed(self): self.watcher.next_etcd_index = 1 responses = [ etcd.EtcdClusterIdChanged(), ] self.m_client.read.side_effect = iter(responses) self.assertRaises(ResyncRequired, self.watcher.wait_for_etcd_event) def test_wait_for_etcd_event_index_cleared(self): self.watcher.next_etcd_index = 1 responses = [ etcd.EtcdEventIndexCleared(), ] self.m_client.read.side_effect = iter(responses) self.assertRaises(ResyncRequired, self.watcher.wait_for_etcd_event) @patch("time.sleep", autospec=True) def test_wait_for_etcd_event_unexpected_error(self, m_sleep): self.watcher.next_etcd_index = 1 responses = [ etcd.EtcdException(), ] self.m_client.read.side_effect = iter(responses) self.assertRaises(ResyncRequired, self.watcher.wait_for_etcd_event) self.assertEqual(m_sleep.mock_calls, [call(1)]) def test_coverage(self): # These methods are no-ops. self.watcher._on_pre_resync() self.watcher._on_snapshot_loaded(Mock()) def tearDown(self): self.reconnect_patch.stop() super(TestEtcdWatcher, self).tearDown()