# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Lint as: python3 """cdc_rsync connection test.""" from concurrent import futures import socket import time from integration_tests.framework import utils from integration_tests.cdc_rsync import test_base RETURN_CODE_SUCCESS = 0 RETURN_CODE_GENERIC_ERROR = 1 RETURN_CODE_CONNECTION_TIMEOUT = 2 RETURN_CODE_ADDRESS_IN_USE = 4 FIRST_PORT = 44450 LAST_PORT = 44459 class ConnectionTest(test_base.CdcRsyncTest): """cdc_rsync connection test class.""" def test_valid_instance(self): """Runs rsync with --instance option for a valid id. 1) Uploads a file with --instance option instead of --ip --port. 2) Checks the file exists on the used instance. """ utils.create_test_file(self.local_data_path, 1024) res = utils.run_rsync(self.local_data_path, self.remote_base_dir) self._assert_rsync_success(res) self.assertTrue(utils.does_file_exist_remotely(self.remote_data_path)) def test_invalid_instance(self): """Runs rsync with --instance option for an invalid id. 1) Uploads a file with --instance option for a non-existing id. 2) Checks the error message. """ bad_host = 'bad_host' utils.create_test_file(self.local_data_path, 1024) res = utils.run_rsync(self.local_data_path, bad_host + ":" + self.remote_base_dir) self.assertEqual(res.returncode, RETURN_CODE_GENERIC_ERROR) self.assertIn('lost connection', str(res.stderr)) def test_contimeout(self): """Runs rsync with --contimeout option for an invalid ip. 1) Uploads a file with bad IP address. 2) Checks the error message and that it timed out after ~5 seconds. 3) Uploads a file with bad IP address and --contimeout 1. 4) Checks the error message and that it timed out after ~1 second. """ utils.create_test_file(self.local_data_path, 1024) bad_host = '192.0.2.1' start = time.time() res = utils.run_rsync(self.local_data_path, bad_host + ":" + self.remote_base_dir) elapsed_time = time.time() - start self.assertGreater(elapsed_time, 4.5) self.assertEqual(res.returncode, RETURN_CODE_CONNECTION_TIMEOUT) self.assertIn('Error: Server connection timed out', str(res.stderr)) start = time.time() res = utils.run_rsync(self.local_data_path, bad_host + ":" + self.remote_base_dir, '--contimeout=1') elapsed_time = time.time() - start self.assertLess(elapsed_time, 3) self.assertEqual(res.returncode, RETURN_CODE_CONNECTION_TIMEOUT) self.assertIn('Error: Server connection timed out', str(res.stderr)) def test_multiple_instances(self): """Runs multiple instances of rsync at the same time.""" num_instances = LAST_PORT - FIRST_PORT + 1 local_data_paths = [] for n in range(num_instances): path = self.local_base_dir + ('testdata_%i.dat' % n) utils.create_test_file(path, 1024) local_data_paths.append(path) with futures.ThreadPoolExecutor(max_workers=num_instances) as executor: res = [] for n in range(num_instances): res.append( executor.submit(utils.run_rsync, local_data_paths[n], self.remote_base_dir)) for r in res: self._assert_rsync_success(r.result()) def test_address_in_use(self): """Blocks all ports and checks that rsync fails with the expected error.""" sockets = [] try: # Occupy all ports. for port in range(FIRST_PORT, LAST_PORT + 1): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sockets.append(s) s.bind(('127.0.0.1', port)) s.listen() # rsync shouldn't be able to find an available port now. utils.create_test_file(self.local_data_path, 1024) res = utils.run_rsync(self.local_data_path, self.remote_base_dir) self.assertIn('All ports are already in use', str(res.stderr)) finally: for s in sockets: s.close() if __name__ == '__main__': test_base.test_base.main()