def test_add_user_already_existing(self, _): with patch("azurelinuxagent.ga.remoteaccess.get_osutil", return_value=MockOSUtil()): rah = RemoteAccessHandler(Mock()) tstpassword = "******" tstuser = "******" expiration_date = datetime.utcnow() + timedelta(days=1) pwd = tstpassword rah._add_user(tstuser, pwd, expiration_date) users = get_user_dictionary(rah._os_util.get_users()) self.assertTrue(tstuser in users, "{0} missing from users".format(tstuser)) self.assertEqual(1, len(users.keys())) actual_user = users[tstuser] self.assertEqual(actual_user[7], (expiration_date + timedelta(days=1)).strftime("%Y-%m-%d")) # add the new duplicate user, ensure it's not created and does not overwrite the existing user. # this does not test the user add function as that's mocked, it tests processing skips the remaining # calls after the initial failure new_user_expiration = datetime.utcnow() + timedelta(days=5) self.assertRaises(Exception, rah._add_user, tstuser, pwd, new_user_expiration) # refresh users users = get_user_dictionary(rah._os_util.get_users()) self.assertTrue( tstuser in users, "{0} missing from users after dup user attempted".format( tstuser)) self.assertEqual(1, len(users.keys())) actual_user = users[tstuser] self.assertEqual(actual_user[7], (expiration_date + timedelta(days=1)).strftime("%Y-%m-%d"))
def test_valid_routes(self): routing_table = \ 'Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT \n' \ 'eth0\t00000000\tC1BB910A\t0003\t0\t0\t0\t00000000\t0\t0\t0 \n' \ 'eth0\tC0BB910A\t00000000\t0001\t0\t0\t0\tC0FFFFFF\t0\t0\t0 \n' \ 'eth0\t10813FA8\tC1BB910A\t000F\t0\t0\t0\tFFFFFFFF\t0\t0\t0 \n' \ 'eth0\tFEA9FEA9\tC1BB910A\t0007\t0\t0\t0\tFFFFFFFF\t0\t0\t0 \n' \ 'docker0\t002BA8C0\t00000000\t0001\t0\t0\t10\t00FFFFFF\t0\t0\t0 \n' known_sha1_hash = b'\x1e\xd1k\xae[\xf8\x9b\x1a\x13\xd0\xbbT\xa4\xe3Y\xa3\xdd\x0b\xbd\xa9' mo = mock.mock_open(read_data=routing_table) with patch(open_patch(), mo): raw_route_list = osutil.DefaultOSUtil().read_route_table() self.assertEqual(len(raw_route_list), 6) self.assertEqual(textutil.hash_strings(raw_route_list), known_sha1_hash) route_list = osutil.DefaultOSUtil().get_list_of_routes(raw_route_list) self.assertEqual(len(route_list), 5) self.assertEqual(route_list[0].gateway_quad(), '10.145.187.193') self.assertEqual(route_list[1].gateway_quad(), '0.0.0.0') self.assertEqual(route_list[1].mask_quad(), '255.255.255.192') self.assertEqual(route_list[2].destination_quad(), '168.63.129.16') self.assertEqual(route_list[1].flags, 1) self.assertEqual(route_list[2].flags, 15) self.assertEqual(route_list[3].flags, 7) self.assertEqual(route_list[3].metric, 0) self.assertEqual(route_list[4].metric, 10) self.assertEqual(route_list[0].interface, 'eth0') self.assertEqual(route_list[4].interface, 'docker0')
def test_read_output_should_return_no_content(self): with patch( 'azurelinuxagent.common.utils.extensionprocessutil.TELEMETRY_MESSAGE_MAX_LEN', 0): expected = "" actual = read_output(self.stdout, self.stderr) self.assertEqual(expected, actual)
def test_dhcp_skip_cache(self): handler = dhcp.get_dhcp_handler() handler.osutil = osutil.DefaultOSUtil() with patch('os.path.exists', return_value=False): with patch.object(osutil.DefaultOSUtil, 'get_dhcp_lease_endpoint')\ as patch_dhcp_cache: with patch.object(dhcp.DhcpHandler, 'send_dhcp_req') \ as patch_dhcp_send: endpoint = 'foo' patch_dhcp_cache.return_value = endpoint # endpoint comes from cache self.assertFalse(handler.skip_cache) handler.run() self.assertTrue(patch_dhcp_cache.call_count == 1) self.assertTrue(patch_dhcp_send.call_count == 0) self.assertTrue(handler.endpoint == endpoint) # reset handler.skip_cache = True handler.endpoint = None # endpoint comes from dhcp request self.assertTrue(handler.skip_cache) handler.run() self.assertTrue(patch_dhcp_cache.call_count == 1) self.assertTrue(patch_dhcp_send.call_count == 1)
def test_collect_and_send_with_call_wireserver_returns_http_error( self, mock_lib_dir, *args): mock_lib_dir.return_value = self.lib_dir fileutil.mkdir(self.event_dir) add_event(name="MonitorTests", op=WALAEventOperation.HeartBeat, is_success=True, message="Test heartbeat") with _create_monitor_handler( enabled_operations=["collect_and_send_events" ]) as monitor_handler: def http_post_handler(url, _, **__): if self.is_telemetry_request(url): return HttpError("A test exception") return None monitor_handler.get_mock_wire_protocol().set_http_handlers( http_post_handler=http_post_handler) with patch("azurelinuxagent.common.logger.warn") as mock_warn: monitor_handler.run_and_wait() self.assertEqual(1, mock_warn.call_count) self.assertEqual(0, len(os.listdir(self.event_dir)))
def _assert_validation(self, http_status_code, http_response, expected_valid, expected_response): test_subject = imds.ImdsClient(restutil.KNOWN_WIRESERVER_IP) with patch("azurelinuxagent.common.utils.restutil.http_get" ) as mock_http_get: mock_http_get.return_value = ResponseMock(status=http_status_code, reason='reason', response=http_response) validate_response = test_subject.validate() self.assertEqual(1, mock_http_get.call_count) positional_args, kw_args = mock_http_get.call_args self.assertTrue('User-Agent' in kw_args['headers']) self.assertEqual(restutil.HTTP_USER_AGENT_HEALTH, kw_args['headers']['User-Agent']) self.assertTrue('Metadata' in kw_args['headers']) self.assertEqual(True, kw_args['headers']['Metadata']) self.assertEqual( 'http://169.254.169.254/metadata/instance?api-version=2018-02-01', positional_args[0]) self.assertEqual(expected_valid, validate_response[0]) self.assertTrue( expected_response in validate_response[1], "Expected: '{0}', Actual: '{1}'".format(expected_response, validate_response[1]))
def test_start_extension_command_should_start_tracking_the_extension_cgroups( self): # CPU usage is initialized when we begin tracking a CPU cgroup; since this test does not retrieve the # CPU usage, there is no need for initialization with patch( "azurelinuxagent.common.cgroup.CpuCgroup.initialize_cpu_usage" ): CGroupConfigurator.get_instance().start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="date", timeout=300, shell=False, cwd=self.tmp_dir, env={}, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.assertTrue( CGroupsTelemetry.is_tracked( os.path.join( self.cgroups_file_system_root, "cpu", "walinuxagent.extensions/Microsoft.Compute.TestExtension_1.2.3" ))) self.assertTrue( CGroupsTelemetry.is_tracked( os.path.join( self.cgroups_file_system_root, "memory", "walinuxagent.extensions/Microsoft.Compute.TestExtension_1.2.3" )))
def test_start_extension_command_should_use_systemd_and_not_the_fallback_option_if_successful( self, _): self.assertTrue(i_am_root(), "Test does not run when non-root") with tempfile.TemporaryFile(dir=self.tmp_dir, mode="w+b") as stdout: with tempfile.TemporaryFile(dir=self.tmp_dir, mode="w+b") as stderr: with patch("azurelinuxagent.common.cgroupapi.subprocess.Popen", wraps=subprocess.Popen) \ as patch_mock_popen: extension_cgroups, process_output = SystemdCgroupsApi( ).start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="date", timeout=300, shell=True, cwd=self.tmp_dir, env={}, stdout=stdout, stderr=stderr) # We should have invoked the extension command only once and succeeded self.assertEquals(1, patch_mock_popen.call_count) args = patch_mock_popen.call_args[0][0] self.assertIn("systemd-run --unit", args) self.assert_cgroups_created(extension_cgroups)
def test_create_should_return_a_FileSystemCgroupsApi_on_non_systemd_platforms( self): with patch("azurelinuxagent.common.cgroupapi.CGroupsApi._is_systemd", return_value=False): api = CGroupsApi.create() self.assertTrue(type(api) == FileSystemCgroupsApi)
def test_foreach_controller_should_handle_errors_in_individual_controllers( self): successful_controllers = [] def controller_operation(controller): if controller == 'cpu': raise Exception('A test exception') successful_controllers.append(controller) with patch("azurelinuxagent.common.cgroupapi.logger.warn" ) as mock_logger_warn: CGroupsApi._foreach_controller(controller_operation, 'A dummy message') self.assertIn( 'memory', successful_controllers, 'The operation was not executed on the memory controller') self.assertEqual( len(successful_controllers), 1, 'The operation was not executed on unexpected controllers: {0}' .format(successful_controllers)) args, kwargs = mock_logger_warn.call_args (message_format, controller, error, message) = args self.assertEquals(message_format, 'Error in cgroup controller "{0}": {1}. {2}') self.assertEquals(controller, 'cpu') self.assertEquals(error, 'A test exception') self.assertEquals(message, 'A dummy message')
def test_remove_extension_cgroups_should_log_a_warning_when_the_cgroup_contains_active_tasks( self): api = FileSystemCgroupsApi() api.create_extension_cgroups_root() api.create_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3") with patch("azurelinuxagent.common.cgroupapi.logger.warn" ) as mock_logger_warn: with patch("azurelinuxagent.common.cgroupapi.os.rmdir", side_effect=OSError(16, "Device or resource busy")): api.remove_extension_cgroups( "Microsoft.Compute.TestExtension-1.2.3") args, kwargs = mock_logger_warn.call_args message = args[0] self.assertIn("still has active tasks", message)
def test_handle_remote_access_remove_and_add(self, _): with patch("azurelinuxagent.ga.remoteaccess.get_osutil", return_value=MockOSUtil()): rah = RemoteAccessHandler(Mock()) data_str = load_data('wire/remote_access_10_accounts.xml') remote_access = RemoteAccess(data_str) count = 0 for user in remote_access.user_list.users: count += 1 user.name = "tstuser{0}".format(count) expiration_date = datetime.utcnow() + timedelta(days=count) user.expiration = expiration_date.strftime( "%a, %d %b %Y %H:%M:%S ") + "UTC" rah._remote_access = remote_access rah._handle_remote_access() users = rah._os_util.get_users() self.assertEqual(10, len(users)) # now remove the user from RemoteAccess new_user = "******" deleted_user = rah._remote_access.user_list.users[3] rah._remote_access.user_list.users[3].name = new_user rah._handle_remote_access() users = rah._os_util.get_users() self.assertTrue(deleted_user not in users, "{0} still in users".format(deleted_user)) self.assertTrue(new_user in [u[0] for u in users], "user {0} not in users".format(new_user)) self.assertEqual(10, len(users))
def test_remove_user_not_exists(self): with patch("azurelinuxagent.ga.remoteaccess.get_osutil", return_value=MockOSUtil()): rah = RemoteAccessHandler(Mock()) user = "******" error = "test exception, user does not exist to delete" self.assertRaisesRegex(Exception, error, rah._remove_user, user)
def test_handle_remote_access_deleted_user_readded(self, _): with patch("azurelinuxagent.ga.remoteaccess.get_osutil", return_value=MockOSUtil()): rah = RemoteAccessHandler(Mock()) data_str = load_data('wire/remote_access_single_account.xml') remote_access = RemoteAccess(data_str) tstuser = remote_access.user_list.users[0].name expiration_date = datetime.utcnow() + timedelta(days=1) expiration = expiration_date.strftime( "%a, %d %b %Y %H:%M:%S ") + "UTC" remote_access.user_list.users[0].expiration = expiration rah._remote_access = remote_access rah._handle_remote_access() users = get_user_dictionary(rah._os_util.get_users()) self.assertTrue(tstuser in users, "{0} missing from users".format(tstuser)) os_util = rah._os_util os_util.__class__ = MockOSUtil os_util.all_users.clear() # pylint: disable=no-member # refresh users users = get_user_dictionary(rah._os_util.get_users()) self.assertTrue(tstuser not in users) rah._handle_remote_access() # refresh users users = get_user_dictionary(rah._os_util.get_users()) self.assertTrue(tstuser in users, "{0} missing from users".format(tstuser))
def test_it_should_log_command_failures_as_errors(self): return_code = 99 command = "exit {0}".format(return_code) with patch("azurelinuxagent.common.utils.shellutil.logger", autospec=True) as mock_logger: shellutil.run_get_output(command, log_cmd=False) self.assertEqual(mock_logger.error.call_count, 1) args, _ = mock_logger.error.call_args message = args[ 0] # message is similar to "Command: [exit 99], return code: [99], result: []" self.assertIn("[{0}]".format(command), message) self.assertIn("[{0}]".format(return_code), message) self.assertEqual( mock_logger.info.call_count, 0, "Did not expect any info messages. Got: {0}".format( mock_logger.info.call_args_list)) self.assertEqual( mock_logger.warn.call_count, 0, "Did not expect any warnings. Got: {0}".format( mock_logger.warn.call_args_list))
def test_start_extension_command_should_not_use_fallback_option_if_extension_fails( self, *args): self.assertTrue(i_am_root(), "Test does not run when non-root") with tempfile.TemporaryFile(dir=self.tmp_dir, mode="w+b") as stdout: with tempfile.TemporaryFile(dir=self.tmp_dir, mode="w+b") as stderr: with patch("azurelinuxagent.common.cgroupapi.subprocess.Popen", wraps=subprocess.Popen) \ as patch_mock_popen: with self.assertRaises(ExtensionError) as context_manager: SystemdCgroupsApi().start_extension_command( extension_name= "Microsoft.Compute.TestExtension-1.2.3", command="ls folder_does_not_exist", timeout=300, shell=True, cwd=self.tmp_dir, env={}, stdout=stdout, stderr=stderr) # We should have invoked the extension command only once, in the systemd-run case self.assertEquals(1, patch_mock_popen.call_count) args = patch_mock_popen.call_args[0][0] self.assertIn("systemd-run --unit", args) self.assertEquals( context_manager.exception.code, ExtensionErrorCodes.PluginUnknownFailure) self.assertIn("Non-zero exit code", ustr(context_manager.exception))
def test_archive03(self): """ All archives should be purged, both with the new naming (with incarnation number) and with the old naming. """ start = datetime.now() timestamp1 = start + timedelta(seconds=5) timestamp2 = start + timedelta(seconds=10) dir_old = timestamp1.isoformat() dir_new = "{0}_incarnation_1".format(timestamp2.isoformat()) archive_old = "{0}.zip".format(timestamp1.isoformat()) archive_new = "{0}_incarnation_1.zip".format(timestamp2.isoformat()) self._write_file( os.path.join("history", dir_old, "Prod.0.manifest.xml")) self._write_file( os.path.join("history", dir_new, "Prod.1.manifest.xml")) self._write_file(os.path.join("history", archive_old)) self._write_file(os.path.join("history", archive_new)) self.assertEqual(4, len(os.listdir(self.history_dir)), "Not all entries were archived!") test_subject = StateArchiver(self.tmp_dir) with patch("azurelinuxagent.common.utils.archive._MAX_ARCHIVED_STATES", 0): test_subject.purge() archived_entries = os.listdir(self.history_dir) self.assertEqual(0, len(archived_entries), "Not all entries were purged!")
def test_start_extension_command_should_not_use_systemd_when_cgroups_are_not_enabled( self, _): configurator = CGroupConfiguratorSystemdTestCase._get_new_cgroup_configurator_instance( ) configurator.disable() with patch("azurelinuxagent.common.cgroupapi.subprocess.Popen", wraps=subprocess.Popen) as patcher: configurator.start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="date", timeout=300, shell=False, cwd=self.tmp_dir, env={}, stdout=subprocess.PIPE, stderr=subprocess.PIPE) command_calls = [ args[0] for args, _ in patcher.call_args_list if len(args) > 0 and "date" in args[0] ] self.assertEqual( len(command_calls), 1, "The test command should have been called exactly once [{0}]". format(command_calls)) self.assertNotIn( "systemd-run", command_calls[0], "The command should not have been invoked using systemd") self.assertEqual(command_calls[0], "date", "The command line should not have been modified")
def test_cgroup_operations_should_not_invoke_the_cgroup_api_when_cgroups_are_not_enabled( self): configurator = CGroupConfigurator.get_instance() configurator.disable() # List of operations to test, and the functions to mock used in order to do verifications operations = [ [ lambda: configurator.create_agent_cgroups(track_cgroups=False), "azurelinuxagent.common.cgroupapi.FileSystemCgroupsApi.create_agent_cgroups" ], [ lambda: configurator.cleanup_legacy_cgroups(), "azurelinuxagent.common.cgroupapi.FileSystemCgroupsApi.cleanup_legacy_cgroups" ], [ lambda: configurator.create_extension_cgroups_root(), "azurelinuxagent.common.cgroupapi.FileSystemCgroupsApi.create_extension_cgroups_root" ], [ lambda: configurator.create_extension_cgroups("A.B.C-1.0.0"), "azurelinuxagent.common.cgroupapi.FileSystemCgroupsApi.create_extension_cgroups" ], [ lambda: configurator.remove_extension_cgroups("A.B.C-1.0.0"), "azurelinuxagent.common.cgroupapi.FileSystemCgroupsApi.remove_extension_cgroups" ] ] for op in operations: with patch(op[1]) as mock_cgroup_api_operation: op[0]() self.assertEqual(mock_cgroup_api_operation.call_count, 0)
def test_start_extension_command_should_use_systemd_run_when_cgroups_are_enabled( self, _): with mock_cgroup_commands(): with patch("azurelinuxagent.common.cgroupapi.subprocess.Popen", wraps=subprocess.Popen) as popen_patch: CGroupConfiguratorSystemdTestCase._get_new_cgroup_configurator_instance( ).start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="the-test-extension-command", timeout=300, shell=False, cwd=self.tmp_dir, env={}, stdout=subprocess.PIPE, stderr=subprocess.PIPE) command_calls = [ args[0] for (args, _) in popen_patch.call_args_list if "the-test-extension-command" in args[0] ] self.assertEqual( len(command_calls), 1, "The test command should have been called exactly once [{0}]" .format(command_calls)) self.assertIn( "systemd-run --unit=Microsoft.Compute.TestExtension_1.2.3", command_calls[0], "The extension should have been invoked using systemd")
def test_init_should_mount_the_cgroups_file_system(self): with patch( "azurelinuxagent.common.osutil.default.DefaultOSUtil.mount_cgroups" ) as mock_mount_cgroups: CGroupConfigurator.get_instance() self.assertEqual(mock_mount_cgroups.call_count, 1)
def test_start_extension_command_should_raise_an_exception_when_the_command_cannot_be_started( self): configurator = CGroupConfiguratorSystemdTestCase._get_new_cgroup_configurator_instance( ) original_popen = subprocess.Popen def mock_popen(command_arg, *args, **kwargs): if "test command" in command_arg: raise Exception("A TEST EXCEPTION") return original_popen(command_arg, *args, **kwargs) with patch("azurelinuxagent.common.cgroupapi.subprocess.Popen", side_effect=mock_popen) as patcher: with self.assertRaises(Exception) as context_manager: configurator.start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="test command", timeout=300, shell=False, cwd=self.tmp_dir, env={}, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.assertIn("A TEST EXCEPTION", str(context_manager.exception))
def test_collect_and_send_with_http_post_returning_503( self, mock_lib_dir, *_): mock_lib_dir.return_value = self.lib_dir fileutil.mkdir(self.event_dir) with _create_monitor_handler( enabled_operations=["collect_and_send_events" ]) as monitor_handler: def http_post_handler(url, _, **__): if self.is_telemetry_request(url): return MockHttpResponse( restutil.httpclient.SERVICE_UNAVAILABLE) return None protocol = monitor_handler.get_mock_wire_protocol() protocol.set_http_handlers(http_post_handler=http_post_handler) sizes = [1, 2, 3] # get the powers of 2, and multiple by 1024. for power in sizes: size = 2**power * 1024 self._create_extension_event(size) with patch("azurelinuxagent.common.logger.warn") as mock_warn: monitor_handler.run_and_wait() self.assertEqual(1, mock_warn.call_count) message = "[ProtocolError] [Wireserver Exception] [ProtocolError] [Wireserver Failed] URI http://{0}/machine?comp=telemetrydata [HTTP Failed] Status Code 503".format( protocol.get_endpoint()) self.assertIn(message, mock_warn.call_args[0][0]) self.assertEqual(0, len(os.listdir(self.event_dir)))
def test_it_should_cleanup_files_on_error(self): orig_write_file = fileutil.write_file files_to_fail = [] def mock_write_file(path, _, *__): if files_to_fail[0] in path: raise IOError("Invalid file: {0}".format(path)) return orig_write_file(path, _, *__) self.__replace_popen_cmd = TestPersistFirewallRulesHandler.__mock_network_setup_service_disabled with self._get_persist_firewall_rules_handler() as handler: test_files = [self._binary_file, self._network_service_unit_file] for file_to_fail in test_files: files_to_fail = [file_to_fail] with patch("azurelinuxagent.common.persist_firewall_rules.fileutil.write_file", side_effect=mock_write_file): with self.assertRaises(Exception) as context_manager: handler.setup() self.assertIn("Invalid file: {0}".format(file_to_fail), ustr(context_manager.exception)) self.assertFalse(os.path.exists(file_to_fail), "File should be deleted: {0}".format(file_to_fail)) # Cleanup remaining files for test clarity for test_file in test_files: try: os.remove(test_file) except Exception: pass
def test_error_heartbeat_creates_no_signal(self, patch_report_heartbeat, patch_http_get, patch_add_event, *args): monitor_handler = get_monitor_handler() protocol = WireProtocol('endpoint') protocol.update_goal_state = MagicMock() with patch( 'azurelinuxagent.common.protocol.util.ProtocolUtil.get_protocol', return_value=protocol): monitor_handler.init_protocols() monitor_handler.last_host_plugin_heartbeat = datetime.datetime.utcnow( ) - timedelta(hours=1) patch_http_get.side_effect = IOError('client error') monitor_handler.send_host_plugin_heartbeat() # health report should not be made self.assertEqual(0, patch_report_heartbeat.call_count) # telemetry with failure details is sent self.assertEqual(1, patch_add_event.call_count) self.assertEqual('HostPluginHeartbeat', patch_add_event.call_args[1]['op']) self.assertTrue( 'client error' in patch_add_event.call_args[1]['message']) self.assertEqual(False, patch_add_event.call_args[1]['is_success']) monitor_handler.stop()
def test_sleep_until_next_operation_should_wait_for_the_closest_operation( self): operations = [ PeriodicOperation("one", lambda: None, period=datetime.timedelta(seconds=60)), PeriodicOperation("one", lambda: None, period=datetime.timedelta(hours=1)), PeriodicOperation( "one", lambda: None, period=datetime.timedelta(seconds=10)), # closest operation PeriodicOperation("one", lambda: None, period=datetime.timedelta(minutes=11)), PeriodicOperation("one", lambda: None, period=datetime.timedelta(days=1)) ] for op in operations: # pylint: disable=invalid-name op.run() def mock_sleep(seconds): mock_sleep.seconds = seconds mock_sleep.seconds = 0 with patch("azurelinuxagent.ga.periodic_operation.time.sleep", side_effect=mock_sleep): PeriodicOperation.sleep_until_next_operation(operations) self.assertAlmostEqual(mock_sleep.seconds, 10, 0, "did not sleep for the expected time")
def test_handle_process_completion_should_raise_on_timeout(self): command = "sleep 1m" timeout = 20 with tempfile.TemporaryFile(dir=self.tmp_dir, mode="w+b") as stdout: with tempfile.TemporaryFile(dir=self.tmp_dir, mode="w+b") as stderr: with patch('time.sleep') as mock_sleep: with self.assertRaises(ExtensionError) as context_manager: process = subprocess.Popen( command, # pylint: disable=subprocess-popen-preexec-fn shell=True, cwd=self.tmp_dir, env={}, stdout=stdout, stderr=stderr, preexec_fn=os.setsid) handle_process_completion(process=process, command=command, timeout=timeout, stdout=stdout, stderr=stderr, error_code=42) # We're mocking sleep to avoid prolonging the test execution time, but we still want to make sure # we're "waiting" the correct amount of time before killing the process and raising an exception # Due to an extra call to sleep at some point in the call stack which only happens sometimes, # we are relaxing this assertion to allow +/- 2 sleep calls. self.assertTrue(abs(mock_sleep.call_count - timeout) <= 2) self.assertEqual( context_manager.exception.code, ExtensionErrorCodes.PluginHandlerScriptTimedout) self.assertIn("Timeout({0})".format(timeout), ustr(context_manager.exception))
def test_it_should_read_only_the_head_of_large_outputs(self): command = "produce_long_output.py" self.create_script(os.path.join(self.ext_handler_instance.get_base_dir(), command), ''' import sys sys.stdout.write("O" * 5 * 1024 * 1024) sys.stderr.write("E" * 5 * 1024 * 1024) ''') # Mocking the call to file.read() is difficult, so instead we mock the call to format_stdout_stderr, which takes the # return value of the calls to file.read(). The intention of the test is to verify we never read (and load in memory) # more than a few KB of data from the files used to capture stdout/stderr with patch('azurelinuxagent.common.utils.extensionprocessutil.format_stdout_stderr', side_effect=format_stdout_stderr) as mock_format: output = self.ext_handler_instance.launch_command(command) self.assertGreaterEqual(len(output), 1024) self.assertLessEqual(len(output), TELEMETRY_MESSAGE_MAX_LEN) mock_format.assert_called_once() args, kwargs = mock_format.call_args # pylint: disable=unused-variable stdout, stderr = args self.assertGreaterEqual(len(stdout), 1024) self.assertLessEqual(len(stdout), TELEMETRY_MESSAGE_MAX_LEN) self.assertGreaterEqual(len(stderr), 1024) self.assertLessEqual(len(stderr), TELEMETRY_MESSAGE_MAX_LEN)
def test_log_collector_should_truncate_large_text_files_and_ignore_large_binary_files( self): # Set the size limit so that some files are too large to collect in full. with patch("azurelinuxagent.common.logcollector._FILE_SIZE_LIMIT", SMALL_FILE_SIZE): log_collector = LogCollector() archive = log_collector.collect_logs_and_get_archive() self._assert_archive_created(archive) expected_files = [ os.path.join(self.root_collect_dir, "waagent.log"), self._truncated_path( os.path.join( self.root_collect_dir, "waagent.log.1")), # this file should be truncated os.path.join(self.root_collect_dir, "waagent.log.2.gz"), os.path.join(self.root_collect_dir, "less_important_file"), os.path.join(self.root_collect_dir, "another_dir", "least_important_file") ] unexpected_files = [ os.path.join(self.root_collect_dir, "waagent.log.3.gz" ) # binary files cannot be truncated, ignore it ] self._assert_files_are_in_archive(expected_files) self._assert_files_are_not_in_archive(unexpected_files) no_files = self._get_number_of_files_in_archive() self.assertEqual( 5, no_files, "Expected 5 files in archive, found {0}!".format(no_files))
def test_start_extension_command_should_use_systemd_to_execute_the_command( self, _): with mock_cgroup_commands(): with patch("azurelinuxagent.common.cgroupapi.subprocess.Popen", wraps=subprocess.Popen) as popen_patch: SystemdCgroupsApi().start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="the-test-extension-command", timeout=300, shell=True, cwd=self.tmp_dir, env={}, stdout=subprocess.PIPE, stderr=subprocess.PIPE) extension_calls = [ args[0] for (args, _) in popen_patch.call_args_list if "the-test-extension-command" in args[0] ] self.assertEquals( 1, len(extension_calls), "The extension should have been invoked exactly once") self.assertIn( "systemd-run --unit=Microsoft.Compute.TestExtension_1.2.3", extension_calls[0], "The extension should have been invoked using systemd")