def test_select_neighbors_for_reply_should_not_contain_oldest_peer(self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) neighbors = self.partialView.select_neighbors_for_reply(oldest) self.assertFalse(neighbors.contains(oldest))
def test_get_oldest_peer(self): self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 1)) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) oldest = self.partialView.get_oldest_peer() self.assertEqual(oldest.ip, "172.0.1.5") self.assertEqual(oldest.age, 4)
def test_sample_descriptors_should_return_less_than_limit_peers_if_size_less_than_limit( self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) size = self.partialView.size sample = self.partialView.sample_descriptors(3) self.assertEqual(len(sample), size)
def test_select_neighbors_for_reply_should_return_a_full_view(self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) neighbors = self.partialView.select_neighbors_for_reply(oldest) self.assertTrue(neighbors.is_full()) self.assertEqual(neighbors.size, neighbors.shuffle_length)
def test_sort(self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(PodDescriptor("172.0.1.8", 1)) self.partialView.sort() self.assertEqual(self.partialView.peer_list[0].ip, "172.0.1.8") self.assertEqual(self.partialView.peer_list[1].ip, "172.0.1.5") self.assertEqual(self.partialView.peer_list[2].ip, "172.0.1.4")
def test_select_neighbors_for_reply_should_contain_avoid_peer_if_size_eq_shuffle_length( self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) neighbors = self.partialView.select_neighbors_for_reply(oldest) self.assertTrue(neighbors.is_full()) self.assertEqual(neighbors.size, neighbors.shuffle_length)
def test_sample_ips_should_return_a_list_of_ips(self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(PodDescriptor("172.0.1.10", 8)) limit = 3 sample = self.partialView.sample_ips(limit) self.assertIn("172.0.1.5", sample) self.assertIn("172.0.1.4", sample) self.assertIn("172.0.1.10", sample)
def test_sample_descriptors_with_avoid_peer_not_in_view(self): to_avoid = PodDescriptor("172.0.1.9", 4) self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(PodDescriptor("172.0.1.10", 8)) limit = 3 sample = self.partialView.sample_descriptors(limit, to_avoid) self.assertEqual(len(sample), limit) self.assertFalse(to_avoid in sample)
def test_sample_descriptors_with_avoid_peer_less_than_limit(self): to_avoid = PodDescriptor("172.0.1.9", 4) self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(to_avoid) limit = 3 sample = self.partialView.sample_descriptors(limit, to_avoid) self.assertEqual(len(sample), 2) self.assertFalse(to_avoid in sample)
def test_sample_descriptors_should_return_no_more_than_limit_peers(self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 3)) self.partialView.add_peer(PodDescriptor("172.0.1.9", 4)) limit = 2 size = self.partialView.size sample = self.partialView.sample_descriptors(limit) self.assertNotEqual(limit, size) self.assertEqual(len(sample), limit)
def test_select_neighbors_for_request_and_add_peer_should_return_full_view( self): oldest = PodDescriptor("172.0.1.4", 1) self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(oldest) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) neighbors = self.partialView.select_neighbors_for_request(oldest) neighbors.add_peer_ip(self.partialView.ip, allow_self_ip=True) self.assertEqual(neighbors.size, self.partialView.shuffle_length) self.assertTrue(neighbors.is_full())
def test_add_peer_should_return_false_if_peer_already_contained(self): peer = PodDescriptor("A new IP") self.partialView.add_peer(peer) size = self.partialView.size duplicated = PodDescriptor("A new IP") success = self.partialView.add_peer(duplicated) self.assertFalse(success) self.assertEqual(self.partialView.size, size) self.assertEqual(self.partialView.size, len(self.partialView.peer_list))
def setUp(self): self.partialView = PartialView("172.0.1.0") self.descriptors = [] self.ips = [ "172.0.1.1", "172.0.1.2", "172.0.1.3", "172.0.1.4", "172.0.1.5" ] for ip in self.ips: self.descriptors.append(PodDescriptor(ip))
def test_add_peer_should_increment_size_if_not_full(self): size = self.partialView.size peer = PodDescriptor("A new IP") self.partialView.add_peer(peer) self.assertTrue(self.partialView.contains(peer)) self.assertEqual(self.partialView.size, size + 1) self.assertEqual(self.partialView.size, len(self.partialView.peer_list))
def test_exchange_views(self): p1 = PartialView("First IP", 4, 3) p1.add_peer(PodDescriptor("172.0.1.6", 0)) p1.add_peer(PodDescriptor("172.0.1.3", 2)) p1.add_peer(PodDescriptor("172.0.1.5", 3)) p1.add_peer(PodDescriptor("Second IP", 5)) p2 = PartialView("Second IP", 4, 3) p2.add_peer(PodDescriptor("172.0.1.3", 0)) p2.add_peer(PodDescriptor("172.0.1.5", 1)) p2.add_peer(PodDescriptor("172.0.1.2", 2)) p2.add_peer(PodDescriptor("172.0.1.1", 4)) ######################## # P1 starts the exchange ######################## # 1) Increase by one the age of all neighbors p1.increment() # 2) Select neighbor Q with the highest age among all neighbors. oldest = p1.get_oldest_peer() # 3) Select l - 1 other random neighbors (meaning avoid oldest). request = p1.select_neighbors_for_request(oldest) # 4) Replace Q's entry with a new entry of age 0 and with P's address. request.add_peer_ip(p1.ip, allow_self_ip=True) self.assertTrue(request.is_full()) self.assertEqual(request.size, p1.shuffle_length) ################################################ # P2 receives neighbors and prepares a reply ################################################ reply = p2.select_neighbors_for_reply() self.assertTrue(request.is_full()) self.assertEqual(request.size, p1.shuffle_length) # Note that in p1 the oldest is p2 # p1 and p2 know two peers in common # p2 does not have an entry with p1's ip # p1.merge should: # - Discard 172.0.1.3 and 172.0.1.5 # - Put in unknown list 172.0.1.2, 172.0.1.1 # 6) I remove the oldest peer from my view p1.remove_peer(oldest) p1.merge(request, reply) self.assertTrue(p1.is_full()) for peer in reply.get_peer_list(): self.assertTrue(p1.contains(peer)) self.assertLessEqual(self.partialView.size, self.partialView.limit)
def test_add_peer_with_allow_self_should_allow_self_entry(self): ip = "my ip" p1 = PartialView(ip) peer = PodDescriptor(ip) size = self.partialView.size success = p1.add_peer(peer, True) self.assertTrue(success) self.assertTrue(p1.contains_ip(ip)) self.assertEqual(p1.size, size + 1)
def test_add_peer_should_not_increment_size_if_full(self): peer = PodDescriptor("A new IP") for i in range(self.partialView.limit): self.partialView.add_peer(self.descriptors[i]) size = self.partialView.size success = self.partialView.add_peer(peer) self.assertFalse(success) self.assertFalse(self.partialView.contains(peer)) self.assertEqual(self.partialView.size, size) self.assertEqual(self.partialView.size, len(self.partialView.peer_list))
def bootstrap_exponential_backoff(self, initial_delay, delay): logger.debug("Init", ip=self.ip, partialView=self.partialView) time.sleep(initial_delay) app_name = os.environ['APP'] attempt = 1 ips = self.k8s.list_pods_ips_by_field_selector( label_selector="app=" + app_name, field_selector="status.phase=Running") logger.debug("Bootstrapping", running_pods=ips, attempt=attempt) # Exponential backoff starts in case the number of running pods is lower than the partialView's limit. # TODO: Did I consider also that some pods might not be ready yet? # TODO: Consider that there is no need to have at least self.partialView.limit peers ready to start! # TODO: There can be peers running with an initial partial view of size < self.partialView.limit while len(ips) <= self.partialView.limit: attempt += 1 delay *= 2 time.sleep(delay) ips = self.k8s.list_pods_ips_by_field_selector( label_selector="app=epto", field_selector="status.phase=Running") logger.debug("Bootstrapping", running_pods=ips, attempt=attempt) # I populate the PartialView and I avoid to consider myself try: ips.remove(self.ip) except ValueError: logger.debug("self.ip was not there") while not self.partialView.is_full(): random_ip = random.choice(ips) # TODO: REPLACE WITH self.partialView.add_peer_ip(random_ip) self.partialView.add_peer( PodDescriptor(random_ip, random.randint(0, 9))) logger.debug("Bootstrapping", partialView=self.partialView)
def test_increment(self): self.partialView.add_peer(PodDescriptor("172.0.1.5", 1)) self.partialView.add_peer(PodDescriptor("172.0.1.7", 3)) self.partialView.increment() self.assertEqual(self.partialView.peer_list[0].age, 2) self.assertEqual(self.partialView.peer_list[1].age, 4)
def test_initial_age_peer(self): self.partialView.add_peer(PodDescriptor("172.0.1.5")) self.assertEqual(self.partialView.peer_list[0].age, 0)
def test_get_oldest_peer_should_return_a_pod_descriptor(self): self.partialView.add_peer(PodDescriptor("172.0.1.6", 2)) self.partialView.add_peer(PodDescriptor("172.0.1.4", 1)) self.partialView.add_peer(PodDescriptor("172.0.1.5", 4)) oldest = self.partialView.get_oldest_peer() self.assertTrue(isinstance(oldest, PodDescriptor))