def test_network_disconnect_primary(self): # Application operation fails against primary. Test that topology # type changes from ReplicaSetWithPrimary to ReplicaSetNoPrimary. # http://bit.ly/1B5ttuL primary, secondary = servers = [MockupDB() for _ in range(2)] for server in servers: server.run() self.addCleanup(server.stop) hosts = [server.address_string for server in servers] primary_response = OpReply(ismaster=True, setName='rs', hosts=hosts, minWireVersion=2, maxWireVersion=6) primary.autoresponds('ismaster', primary_response) secondary.autoresponds( 'ismaster', ismaster=False, secondary=True, setName='rs', hosts=hosts, minWireVersion=2, maxWireVersion=6) client = MongoClient(primary.uri, replicaSet='rs') self.addCleanup(client.close) wait_until(lambda: client.primary == primary.address, 'discover primary') topology = client._topology self.assertEqual(TOPOLOGY_TYPE.ReplicaSetWithPrimary, topology.description.topology_type) # Open a socket in the application pool (calls ismaster). with going(client.db.command, 'buildinfo'): primary.receives('buildinfo').ok() # The primary hangs replying to ismaster. ismaster_future = Future() primary.autoresponds('ismaster', lambda r: r.ok(ismaster_future.result())) # Network error on application operation. with self.assertRaises(ConnectionFailure): with going(client.db.command, 'buildinfo'): primary.receives('buildinfo').hangup() # Topology type is updated. self.assertEqual(TOPOLOGY_TYPE.ReplicaSetNoPrimary, topology.description.topology_type) # Let ismasters through again. ismaster_future.set_result(primary_response) # Demand a primary. with going(client.db.command, 'buildinfo'): wait_until(lambda: client.primary == primary.address, 'rediscover primary') primary.receives('buildinfo').ok() self.assertEqual(TOPOLOGY_TYPE.ReplicaSetWithPrimary, topology.description.topology_type)
def test_client_handshake_saslSupportedMechs(self): server = MockupDB() server.run() self.addCleanup(server.stop) primary_response = OpReply('ismaster', True, minWireVersion=2, maxWireVersion=6) client = MongoClient(server.uri, username='******', password='******') self.addCleanup(client.close) # New monitoring sockets send data during handshake. heartbeat = server.receives('ismaster') heartbeat.ok(primary_response) future = go(client.db.command, 'whatever') for request in server: if request.matches('ismaster'): if request.client_port == heartbeat.client_port: # This is the monitor again, keep going. request.ok(primary_response) else: # Handshaking a new application socket should send # saslSupportedMechs and speculativeAuthenticate. self.assertEqual(request['saslSupportedMechs'], 'admin.username') self.assertIn( 'saslStart', request['speculativeAuthenticate']) auth = {'conversationId': 1, 'done': False, 'payload': b'r=wPleNM8S5p8gMaffMDF7Py4ru9bnmmoqb0' b'1WNPsil6o=pAvr6B1garhlwc6MKNQ93ZfFky' b'tXdF9r,s=4dcxugMJq2P4hQaDbGXZR8uR3ei' b'PHrSmh4uhkg==,i=15000'} request.ok('ismaster', True, saslSupportedMechs=['SCRAM-SHA-256'], speculativeAuthenticate=auth, minWireVersion=2, maxWireVersion=6) # Authentication should immediately fail with: # OperationFailure: Server returned an invalid nonce. with self.assertRaises(OperationFailure): future() return
def test_client_handshake_data(self): primary, secondary = MockupDB(), MockupDB() for server in primary, secondary: server.run() self.addCleanup(server.stop) hosts = [server.address_string for server in primary, secondary] primary_response = OpReply('ismaster', True, setName='rs', hosts=hosts) secondary_response = OpReply('ismaster', False, setName='rs', hosts=hosts, secondary=True) client = MongoClient(primary.uri, replicaSet='rs', appname='my app', heartbeatFrequencyMS=500) # Speed up the test. self.addCleanup(client.close) # New monitoring sockets send data during handshake. heartbeat = primary.receives('ismaster') _check_handshake_data(heartbeat) heartbeat.ok(primary_response) heartbeat = secondary.receives('ismaster') _check_handshake_data(heartbeat) heartbeat.ok(secondary_response) # Subsequent heartbeats have no client data. primary.receives('ismaster', 1, client=absent).ok(primary_response) secondary.receives('ismaster', 1, client=absent).ok(secondary_response) # After a disconnect, next ismaster has client data again. primary.receives('ismaster', 1, client=absent).hangup() heartbeat = primary.receives('ismaster') _check_handshake_data(heartbeat) heartbeat.ok(primary_response) secondary.autoresponds('ismaster', secondary_response) # Start a command, so the client opens an application socket. future = go(client.db.command, 'whatever') for request in primary: if request.matches(Command('ismaster')): if request.client_port == heartbeat.client_port: # This is the monitor again, keep going. request.ok(primary_response) else: # Handshaking a new application socket. _check_handshake_data(heartbeat) request.ok(primary_response) else: # Command succeeds. request.assert_matches(Command('whatever')) request.ok() assert future() return
def test_client_handshake_data(self): primary, secondary = MockupDB(), MockupDB() for server in primary, secondary: server.run() self.addCleanup(server.stop) hosts = [server.address_string for server in (primary, secondary)] primary_response = OpReply('ismaster', True, setName='rs', hosts=hosts, minWireVersion=2, maxWireVersion=6) error_response = OpReply( 0, errmsg='Cache Reader No keys found for HMAC ...', code=211) secondary_response = OpReply('ismaster', False, setName='rs', hosts=hosts, secondary=True, minWireVersion=2, maxWireVersion=6) client = MongoClient(primary.uri, replicaSet='rs', appname='my app', heartbeatFrequencyMS=500) # Speed up the test. self.addCleanup(client.close) # New monitoring sockets send data during handshake. heartbeat = primary.receives('ismaster') _check_handshake_data(heartbeat) heartbeat.ok(primary_response) heartbeat = secondary.receives('ismaster') _check_handshake_data(heartbeat) heartbeat.ok(secondary_response) # Subsequent heartbeats have no client data. primary.receives('ismaster', 1, client=absent).ok(error_response) secondary.receives('ismaster', 1, client=absent).ok(error_response) # The heartbeat retry has no client data after a command failure. primary.receives('ismaster', 1, client=absent).ok(error_response) secondary.receives('ismaster', 1, client=absent).ok(error_response) # Still no client data. primary.receives('ismaster', 1, client=absent).ok(primary_response) secondary.receives('ismaster', 1, client=absent).ok(secondary_response) # After a disconnect, next ismaster has client data again. primary.receives('ismaster', 1, client=absent).hangup() heartbeat = primary.receives('ismaster') _check_handshake_data(heartbeat) heartbeat.ok(primary_response) secondary.autoresponds('ismaster', secondary_response) # Start a command, so the client opens an application socket. future = go(client.db.command, 'whatever') for request in primary: if request.matches(Command('ismaster')): if request.client_port == heartbeat.client_port: # This is the monitor again, keep going. request.ok(primary_response) else: # Handshaking a new application socket. _check_handshake_data(request) request.ok(primary_response) else: # Command succeeds. if version_tuple >= (3, 7): request.assert_matches(OpMsg('whatever')) else: request.assert_matches(Command('whatever')) request.ok() assert future() return