def test_expireIsEnvironmentSpecific(self): """ExpireNode only updates the Environment set on the Node.""" node = create_node("somewhere", "myservice", "env1") node2 = create_node("somewhere2", "myservice", "env2") disco = create_disco() disco.onMessage(None, NodeActive(node)) disco.onMessage(None, NodeActive(node2)) disco.onMessage(None, NodeExpired(node)) self.assertEqual((knownNodes(disco, "myservice", "env1"), knownNodes(disco, "myservice", "env2")), ([], [node2]))
def test_replaceIsEnvironmentSpecific(self): """ReplaceCluster only updates the Environment set on the Node.""" node = create_node("somewhere", "myservice", "env1") node2 = create_node("somewhere2", "myservice", "env2") node3 = create_node("somewhere3", "myservice", "env2") disco = create_disco() disco.onMessage(None, NodeActive(node)) disco.onMessage(None, NodeActive(node2)) disco.onMessage( None, ReplaceCluster(node3.service, node3.environment, [node3])) self.assertEqual((knownNodes(disco, "myservice", "env1"), knownNodes(disco, "myservice", "env2")), ([node], [node3]))
def test_activeDoesNotMutate(self): """ A resolved Node is not mutated by a new NodeActive for same address. """ disco = create_disco() node = create_node("somewhere") disco.onMessage(None, NodeActive(node)) resolved_node = resolve(disco, "myservice", "1.0") node2 = create_node("somewhere") node2.version = "1.3" disco.onMessage(None, NodeActive(node2)) self.assertEqual(resolved_node.version, "1.0")
def test_activeUpdatesMatchingAddress(self): """NodeActive updates a Node with same address to new version and properties.""" disco = create_disco() node = create_node("somewhere") disco.onMessage(None, NodeActive(node)) node2 = create_node("somewhere") node2.version = "1.7" node2.properties = {"a": 123} disco.onMessage(None, NodeActive(node2)) self.assertEqual(knownNodes(disco, "myservice", "sandbox"), [node2]) resolved = resolve(disco, "myservice", "1.7") self.assertEqual((resolved.version, resolved.properties), ("1.7", { "a": 123 }))
def test_unknownServiceChildArrivesFirst(self): """ If resolution can't return an answer immediately, and the child environment gets a node later on, the resolution gets that result. """ env = _parseEnvironment("parent:child") node = create_node("somewhere", "myservice", "parent:child") disco = create_disco() promise = disco.resolve("myservice", "1.0", env) disco.onMessage(None, NodeActive(node)) self.assertEqual(promise.value().getValue().address, "somewhere") # If a parent node arrives we don't blow up: disco.onMessage( None, NodeActive(create_node("somewhereelse", "myservice", "parent")))
def test_expired(self): """NodeExpired removes a Node from Discovery.""" disco = create_disco() node = create_node("somewhere") disco.onMessage(None, NodeActive(node)) disco.onMessage(None, NodeExpired(node)) self.assertEqual(knownNodes(disco, "myservice", "sandbox"), [])
def test_resolve(self): """Resolve is limited to the specified environment.""" node = create_node("somewhere", "myservice", "env1") node2 = create_node("somewhere2", "myservice", "env2") disco = create_disco() disco.onMessage(None, NodeActive(node)) disco.onMessage(None, NodeActive(node2)) # Do repeatedly in case round robin is somehow tricking us: for i in range(10): self.assertEqual( resolve(disco, "myservice", "1.0", "env1").address, "somewhere") for i in range(10): self.assertEqual( resolve(disco, "myservice", "1.0", "env2").address, "somewhere2")
def run_interaction(children): should_fail = next(failures) failed = [] succeeded = [] self.session.start_interaction() for child in children: if isinstance(child, unicode): # Make sure disco knows about the node: if child in created_services: node = created_services[child] else: node = create_node(child, child) created_services[child] = node self.disco.onMessage(None, NodeActive(node)) # Make sure the child Node is resolved in the interaction self.session.resolve(node.service, "1.0") if should_fail: expected_failed_nodes[node] += 1 failed.append(node) else: expected_success_nodes[node] += 1 succeeded.append(node) else: run_interaction(child) if should_fail: self.session.fail_interaction("OHNO") self.session.finish_interaction() self.connector.advance_time(5.0) # Make sure interaction is sent ws_actor.swallowLogMessages() self.connector.expectInteraction(self, ws_actor, self.session, failed, succeeded)
def test_replace(self): """ ReplaceCluster replaces the contents of a Cluster (collection of Nodes for the same service name). """ disco = create_disco() node1 = create_node("somewhere") node2 = create_node("somewhere2") node3 = create_node("somewhere3") node4 = create_node("somewhere4") disco.onMessage(None, NodeActive(node1)) disco.onMessage(None, NodeActive(node2)) disco.onMessage( None, ReplaceCluster("myservice", SANDBOX_ENV, [node3, node4])) self.assertEqual(knownNodes(disco, "myservice", "sandbox"), [node3, node4])
def test_noInheritanceAfterRegister(self): """ Once a service/version pair have been registered in the child environment, the parent environment isn't used even if there are currently no nodes available in the child. """ node = create_node("somewhere", "myservice", "parent:child") disco = create_disco() disco.onMessage(None, NodeActive(node)) # Uh oh, service went away in the child! disco.onMessage(None, NodeExpired(node)) # But it still exists in parent node2 = create_node("somewhere2", "myservice", "parent") disco.onMessage(None, NodeActive(node2)) # However, since it existed in the child before, we won't get version # from the parent. self.assertEqual(resolve(disco, "myservice", "1.0", "parent:child"), None)
def test_environmentReverseInheritance(self): """ A parent environment doesn't get access to Nodes in the child environment. """ # In the child only node = create_node("somewhere", "myservice", "parent:child") disco = create_disco() disco.onMessage(None, NodeActive(node)) # Parent can't find it self.assertEqual(resolve(disco, "myservice", "1.0", "parent"), None)
def test_environmentInheritanceVersions(self): """ If an Environment has a parent it is checked if there are no Nodes that have ever been registered with that service and version in the child Environment. """ # This one won't work if we need 1.0: node = create_node("somewhere2.0", "myservice", "parent:child") node.version = "2.0" # So we expect to fall back to this: node2 = create_node("somewhere1.0", "myservice", "parent") disco = create_disco() disco.onMessage(None, NodeActive(node)) disco.onMessage(None, NodeActive(node2)) # Do repeatedly in case round robin is somehow tricking us: for i in range(10): self.assertEqual( resolve(disco, "myservice", "1.0", "parent:child").address, "somewhere1.0")
def test_activeDoesNotDisableCircuitBreaker(self): """ If a Node has been disabled by a CircuitBreaker then NodeActive with same Node doesn't re-enable it. """ disco = create_disco() node = create_node("somewhere") disco.onMessage(None, NodeActive(node)) resolved_node = resolve(disco, "myservice", "1.0") # Uh-oh it's a pretty broken node: for i in range(10): resolved_node.failure() node = create_node("somewhere") disco.onMessage(None, NodeActive(node)) resolved_node2 = resolve(disco, "myservice", "1.0") self.assertEqual(resolved_node2, None) resolved_node.success() self.assertNodesEqual(resolve(disco, "myservice", "1.0"), node)
def test_notify(self): """ The notify() API allows getting all events passed to the Discovery instance. """ disco = create_disco() messages = [object(), NodeActive(create_node("hello"))] result = [] disco.notify(result.append) for m in messages: disco.onMessage(None, m) self.assertEqual(messages, result)
def test_resolve(self): """resolve() returns a Node matching an active one.""" disco = create_disco() node = create_node("somewhere") node.properties = {"x": 1} disco.onMessage(None, NodeActive(node)) resolved = resolve(disco, "myservice", "1.0") self.assertEqual((resolved.version, resolved.address, resolved.service, resolved.properties), ("1.0", "somewhere", "myservice", { "x": 1 }))
def test_environmentInheritance(self): """ If an Environment has a parent it is checked if there have never been Nodes with that service name registered in the child Environment. """ # In the parent only node = create_node("somewhere", "myservice", "parent") # In the child node2 = create_node("somewhere2", "myservice2", "parent:child") disco = create_disco() disco.onMessage(None, NodeActive(node)) disco.onMessage(None, NodeActive(node2)) # Do repeatedly in case round robin is somehow tricking us: for i in range(10): self.assertEqual( resolve(disco, "myservice", "1.0", "parent:child").address, "somewhere") for i in range(10): self.assertEqual( resolve(disco, "myservice2", "1.0", "parent:child").address, "somewhere2")
def test_activeTriggersWaitingPromises(self): """ NodeActive causes waiting resolve() Promises to get a Node. """ disco = create_disco() result = [] promise = disco.resolve("myservice", "1.0", SANDBOX_ENV) promise.andThen(result.append) self.assertFalse(result) node = create_node("somewhere") disco.onMessage(None, NodeActive(node)) disco.runtime.dispatcher.pump() self.assertNodesEqual(result[0], node)
def test_replaceDoesNotMutate(self): """ A resolved Node is not mutated by a new ReplaceCluster containing a Node with the same address. """ disco = create_disco() node = create_node("somewhere") disco.onMessage(None, NodeActive(node)) resolved_node = resolve(disco, "myservice", "1.0") node2 = create_node("somewhere") node2.version = "1.3" disco.onMessage(None, ReplaceCluster("myservice", SANDBOX_ENV, [node2])) self.assertEqual(resolved_node.version, "1.0")
def test_nodeCircuitBreaker(self): """success()/failure() enable and disable the Node.""" disco = create_disco() node = create_node("somewhere") disco.onMessage(None, NodeActive(node)) resolved_node = resolve(disco, "myservice", "1.0") avail1 = resolved_node.available() # Default threshold in CircuitBreaker is three failures: resolved_node.failure() resolved_node.failure() resolved_node.failure() avail2 = resolved_node.available() resolved_node.success() avail3 = resolved_node.available() self.assertEqual((avail1, avail2, avail3), (True, False, True))
def execute_step(self, step): command, args = step if command == "add": service, address = args message = NodeActive(self.create_node(address, service)) self.fake.add(service, address) elif command == "remove": service, address = args message = NodeExpired(self.create_node(address, service)) self.fake.remove(service, address) elif command == "replace": service, addresses = args nodes = [ self.create_node(address, service) for address in addresses ] message = ReplaceCluster(service, SANDBOX_ENV, nodes) self.fake.replace(service, addresses) else: raise AssertionError("Unknown command.") self.real.onMessage(None, message) self.fake.compare(self.real)
def add_nodes(self, mdk): """Register existence of nodes with the MDK instance.""" mdk._disco.onMessage(None, NodeActive(self.node1)) mdk._disco.onMessage(None, NodeActive(self.node2)) mdk._disco.onMessage(None, NodeActive(self.node3))
def test_active(self): """NodeActive adds a Node to Discovery.""" disco = create_disco() node = create_node("somewhere") disco.onMessage(None, NodeActive(node)) self.assertEqual(knownNodes(disco, "myservice", "sandbox"), [node])