def test_start_extension_command_should_use_systemd_if_not_failing(self): original_popen = subprocess.Popen def mock_popen(*args, **kwargs): return original_popen(*args, **kwargs) 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", side_effect=mock_popen) as patch_mock_popen: process, extension_cgroups = SystemdCgroupsApi( ).start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="date", shell=True, cwd=self.tmp_dir, env={}, stdout=stdout, stderr=stderr) process.wait() ret = process.poll() # We should have invoked the extension command only once and succeeded self.assertEquals(0, ret) 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_start_extension_command_should_not_use_systemd_if_failing(self): original_popen = subprocess.Popen def mock_popen(*args, **kwargs): # Inject a syntax error to the call systemd_command = args[0].replace('systemd-run', 'systemd-run syntax_error') new_args = (systemd_command, ) return original_popen(new_args, **kwargs) 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.add_event" ) as mock_add_event: with patch( "azurelinuxagent.common.cgroupapi.subprocess.Popen", side_effect=mock_popen) as patch_mock_popen: # We expect this call to fail because of the syntax error process, extension_cgroups = SystemdCgroupsApi( ).start_extension_command( extension_name= "Microsoft.Compute.TestExtension-1.2.3", command="date", shell=True, cwd=self.tmp_dir, env={}, stdout=stdout, stderr=stderr) process.wait() args, kwargs = mock_add_event.call_args self.assertIn( "Failed to run systemd-run for unit Microsoft.Compute.TestExtension_1.2.3", kwargs['message']) self.assertIn( "Failed to find executable syntax_error: No such file or directory", kwargs['message']) self.assertEquals(False, kwargs['is_success']) self.assertEquals('InvokeCommandUsingSystemd', kwargs['op']) # We expect the process to ultimately succeed since the fallback option worked ret = process.poll() self.assertEquals(0, ret) # We expect two calls to Popen, first for the systemd-run call, second for the fallback option self.assertEquals(2, patch_mock_popen.call_count) first_call_args = patch_mock_popen.mock_calls[0][1][0] second_call_args = patch_mock_popen.mock_calls[1][1][0] self.assertIn("systemd-run --unit", first_call_args) self.assertNotIn("systemd-run --unit", second_call_args) # No cgroups should have been created self.assertEquals(extension_cgroups, [])
def test_start_extension_command_should_use_systemd_if_not_failing(self): original_popen = subprocess.Popen def mock_popen(*args, **kwargs): return original_popen(*args, **kwargs) 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", side_effect=mock_popen) as patch_mock_popen: process, extension_cgroups = SystemdCgroupsApi().start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="date", shell=True, cwd=self.tmp_dir, env={}, stdout=stdout, stderr=stderr) process.wait() ret = process.poll() # We should have invoked the extension command only once and succeeded self.assertEquals(0, ret) self.assertEquals(1, patch_mock_popen.call_count) # Assert that the extension's cgroups were created as well self.assertEqual(len(extension_cgroups), 2, 'start_extension_command did not return the expected number of cgroups') cpu_found = memory_found = False for cgroup in extension_cgroups: match = re.match(r'^/sys/fs/cgroup/(cpu|memory)/system.slice/Microsoft.Compute.TestExtension_1\.2\.3\_([a-f0-9-]+)\.scope$', cgroup.path) self.assertTrue(match is not None, "Unexpected path for cgroup: {0}".format(cgroup.path)) if match.group(1) == 'cpu': cpu_found = True if match.group(1) == 'memory': memory_found = True self.assertTrue(cpu_found, 'start_extension_command did not return a cpu cgroup') self.assertTrue(memory_found, 'start_extension_command did not return a memory cgroup')
def test_start_extension_command_should_create_extension_scopes(self): original_popen = subprocess.Popen def mock_popen(*args, **kwargs): return original_popen("date", **kwargs) # we mock subprocess.Popen to execute a dummy command (date), so no actual cgroups are created; their paths # should be computed properly, though with patch("azurelinuxagent.common.cgroupapi.subprocess.Popen", mock_popen): process, extension_cgroups = SystemdCgroupsApi( ).start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="date", shell=False, cwd=self.tmp_dir, env={}, stdout=subprocess.PIPE, stderr=subprocess.PIPE) process.wait() self.assert_cgroups_created(extension_cgroups)
def test_start_extension_command_should_create_extension_scopes(self): original_popen = subprocess.Popen def mock_popen(*args, **kwargs): return original_popen("date", **kwargs) # we mock subprocess.Popen to execute a dummy command (date), so no actual cgroups are created; their paths # should be computed properly, though with patch("azurelinuxagent.common.cgroupapi.subprocess.Popen", mock_popen): process, extension_cgroups = SystemdCgroupsApi().start_extension_command( extension_name="Microsoft.Compute.TestExtension-1.2.3", command="date", shell=False, cwd=self.tmp_dir, env={}, stdout=subprocess.PIPE, stderr=subprocess.PIPE) process.wait() self.assertEqual(len(extension_cgroups), 2, 'start_extension_command did not return the expected number of cgroups') cpu_found = memory_found = False for cgroup in extension_cgroups: match = re.match(r'^/sys/fs/cgroup/(cpu|memory)/system.slice/Microsoft.Compute.TestExtension_1\.2\.3\_([a-f0-9-]+)\.scope$', cgroup.path) self.assertTrue(match is not None, "Unexpected path for cgroup: {0}".format(cgroup.path)) if match.group(1) == 'cpu': cpu_found = True if match.group(1) == 'memory': memory_found = True self.assertTrue(cpu_found, 'start_extension_command did not return a cpu cgroup') self.assertTrue(memory_found, 'start_extension_command did not return a memory cgroup')
def test_start_extension_command_should_capture_only_the_last_subprocess_output( self): original_popen = subprocess.Popen def mock_popen(*args, **kwargs): # Inject a syntax error to the call systemd_command = args[0].replace('systemd-run', 'systemd-run syntax_error') new_args = (systemd_command, ) return original_popen(new_args, **kwargs) 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.add_event" ) as mock_add_event: with patch( "azurelinuxagent.common.cgroupapi.subprocess.Popen", side_effect=mock_popen) as patch_mock_popen: # We expect this call to fail because of the syntax error process, extension_cgroups = SystemdCgroupsApi( ).start_extension_command( extension_name= "Microsoft.Compute.TestExtension-1.2.3", command="echo 'very specific test message'", shell=True, cwd=self.tmp_dir, env={}, stdout=stdout, stderr=stderr) process.wait() process_output = read_output(stdout, stderr) self.assertEquals( "[stdout]\nvery specific test message\n\n\n[stderr]\n", process_output)