def run(self, *args, **kwargs): """ Run a shell command :rtype: ~saltfactories.utils.processes.ProcessResult """ check = kwargs.pop("check", True) kwargs.setdefault("cwd", str(self.cwd or self.venv_dir)) kwargs.setdefault("stdout", subprocess.PIPE) kwargs.setdefault("stderr", subprocess.PIPE) kwargs.setdefault("universal_newlines", True) kwargs.setdefault("env", self.environ) proc = subprocess.run(args, check=False, **kwargs) ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) log.debug(ret) if check is True: try: proc.check_returncode() except subprocess.CalledProcessError as exc: # pragma: no cover raise ProcessFailed( "Command failed return code check", cmdline=proc.args, stdout=proc.stdout, stderr=proc.stderr, exitcode=proc.returncode, ) from exc return ret
def test_attributes(): exitcode = 0 stdout = "STDOUT" stderr = "STDERR" cmdline = None ret = ProcessResult(exitcode, stdout, stderr) assert ret.exitcode == exitcode assert ret.stdout == stdout assert ret.stderr == stderr assert ret.cmdline == cmdline cmdline = [1, 2, 3] ret = ProcessResult(exitcode, stdout, stderr, cmdline=cmdline) assert ret.exitcode == exitcode assert ret.stdout == stdout assert ret.stderr == stderr assert ret.cmdline == cmdline
def terminate(self): atexit.unregister(self.terminate) stdout = stderr = None if self.container is None: return ProcessResult(exitcode=0, stdout=None, stderr=None) try: container = self.docker_client.containers.get(self.container.id) logs = container.logs(stdout=True, stderr=True, stream=False) if isinstance(logs, bytes): stdout = logs.decode() else: stdout = logs[0].decode() stderr = logs[1].decode() log.warning("Running Container Logs:\n%s\n%s", stdout, stderr) if container.status == "running": container.remove(force=True) container.wait() except docker.errors.NotFound: pass return ProcessResult(exitcode=0, stdout=stdout, stderr=stderr)
def test_cli_timeout_updates_to_timeout_kw_minus_5( minion_id, config_dir, config_file, cli_script_name ): # _timeout is passed, the value of --timeout is _timeout minus 5 default_timeout = 10 explicit_timeout = 60 config = {"conf_file": config_file, "id": "the-id"} args = ["test.ping"] kwargs = {"minion_tgt": minion_id, "_timeout": explicit_timeout} expected = [ sys.executable, cli_script_name, "--config-dir={}".format(config_dir.strpath), "--timeout={}".format(explicit_timeout - 5), "--out=json", "--out-indent=0", "--log-level=quiet", minion_id, "test.ping", ] popen_mock = mock.MagicMock() popen_mock.pid = os.getpid() popen_mock.poll = mock.MagicMock(side_effect=[None, None, None, None, True]) popen_mock.terminate = mock.MagicMock(return_value=ProcessResult(0, "", "", cmdline=())) terminate_mock = mock.MagicMock(return_value=ProcessResult(0, "", "")) proc = SaltCliFactory( cli_script_name=cli_script_name, config=config, default_timeout=default_timeout ) with mock.patch.object(proc.impl, "init_terminal", popen_mock), mock.patch.object( proc, "terminate", terminate_mock ): proc.impl._terminal = popen_mock # We set __cli_timeout_supported__ to True just to test. This would be an attribute set # at the class level for Salt CLI's that support the timeout flag, like for example, salt-run proc.__cli_timeout_supported__ = True ret = proc.run(*args, **kwargs) assert proc.impl._terminal_timeout == explicit_timeout assert popen_mock.call_args[0][0] == expected # pylint: disable=unsubscriptable-object
def test_str_formatting(): exitcode = 0 stdout = "STDOUT" stderr = "STDERR" cmdline = None ret = ProcessResult(exitcode, stdout, stderr) expected = textwrap.dedent("""\ ProcessResult Exitcode: {} Process Output: >>>>> STDOUT >>>>> {} <<<<< STDOUT <<<<< >>>>> STDERR >>>>> {} <<<<< STDERR <<<<< """.format(exitcode, stdout, stderr)) assert str(ret) == expected cmdline = [1, 2, 3] ret = ProcessResult(exitcode, stdout, stderr, cmdline=cmdline) expected = textwrap.dedent("""\ ProcessResult Command Line: {!r} Exitcode: {} Process Output: >>>>> STDOUT >>>>> {} <<<<< STDOUT <<<<< >>>>> STDERR >>>>> {} <<<<< STDERR <<<<< """.format( cmdline, exitcode, stdout, stderr, )) assert str(ret) == expected
def terminate(self): if self._terminate_result is not None: # The factory is already terminated return self._terminate_result atexit.unregister(self.terminate) for callback, args, kwargs in self.before_terminate_callbacks: try: callback(*args, **kwargs) except Exception as exc: # pylint: disable=broad-except log.info( "Exception raised when running %s: %s", self._format_callback(callback, args, kwargs), exc, exc_info=True, ) stdout = stderr = None try: if self.container is not None: container = self.docker_client.containers.get(self.name) logs = container.logs(stdout=True, stderr=True, stream=False) if isinstance(logs, bytes): stdout = logs.decode() else: stdout = logs[0].decode() stderr = logs[1].decode() if stdout and stderr: log.info("Stopped Container Logs:\n%s\n%s", stdout, stderr) elif stdout: log.info("Stopped Container Logs:\n%s", stdout) if container.status == "running": container.remove(force=True) container.wait() self.container = None except docker.errors.NotFound: pass finally: for callback, args, kwargs in self.after_terminate_callbacks: try: callback(*args, **kwargs) except Exception as exc: # pylint: disable=broad-except log.info( "Exception raised when running %s: %s", self._format_callback(callback, args, kwargs), exc, exc_info=True, ) self._terminate_result = ProcessResult(exitcode=0, stdout=stdout, stderr=stderr) return self._terminate_result
def terminate(self): """ Terminate the started daemon """ if self._terminal is None: return self._terminal_result atexit.unregister(self.terminate) log.info("Stopping %s", self) # Collect any child processes information before terminating the process try: for child in psutil.Process( self._terminal.pid).children(recursive=True): if child not in self._children: self._children.append(child) except psutil.NoSuchProcess: # The terminal process is gone pass # poll the terminal before trying to terminate it, running or not, so that # the right returncode is set on the popen object self._terminal.poll() # Lets log and kill any child processes left behind terminate_process( pid=self._terminal.pid, kill_children=True, children=self._children, slow_stop=self.slow_stop, ) stdout, stderr = self._terminal.communicate() try: log_message = "Terminated {}.".format(self) if stdout or stderr: log_message += " Process Output:" if stdout: log_message += "\n>>>>> STDOUT >>>>>\n{}\n<<<<< STDOUT <<<<<".format( stdout.strip()) if stderr: log_message += "\n>>>>> STDERR >>>>>\n{}\n<<<<< STDERR <<<<<".format( stderr.strip()) log_message += "\n" log.info(log_message) self._terminal_result = ProcessResult(self._terminal.returncode, stdout, stderr, cmdline=self._terminal.args) return self._terminal_result finally: self._terminal = None self._terminal_timeout = None self._children = []
def test_cli_timeout_updates_to_default_timeout_plus_10( minion_id, config_dir, config_file, cli_script_name ): # Neither _timeout nor --timeout are passed, --timeout equals the default timeout, internal timeout is added 10 timeout = 10 config = {"conf_file": config_file, "id": "the-id"} args = ["test.ping"] kwargs = {"minion_tgt": minion_id} expected = [ sys.executable, cli_script_name, "--config-dir={}".format(config_dir), "--timeout={}".format(timeout), "--out=json", "--out-indent=0", "--log-level=critical", minion_id, "test.ping", ] popen_mock = mock.MagicMock() popen_mock.pid = os.getpid() popen_mock.poll = mock.MagicMock(side_effect=[None, None, None, None, True]) popen_mock.terminate = mock.MagicMock(return_value=ProcessResult(0, "", "", cmdline=())) terminate_mock = mock.MagicMock(return_value=ProcessResult(0, "", "")) proc = SaltCli(script_name=cli_script_name, config=config, timeout=timeout) with mock.patch.object(proc.impl, "init_terminal", popen_mock), mock.patch.object( proc, "terminate", terminate_mock ): proc.impl._terminal = popen_mock # We set __cli_timeout_supported__ to True just to test. This would be an attribute set # at the class level for Salt CLI's that support the timeout flag, like for example, salt-run proc.__cli_timeout_supported__ = True ret = proc.run(*args, **kwargs) assert proc.impl._terminal_timeout == timeout + 10 assert popen_mock.call_args[0][0] == expected # pylint: disable=unsubscriptable-object
def run(self, *cmd, **kwargs): if len(cmd) == 1: cmd = cmd[0] log.info("%s is running %r ...", self, cmd) # We force dmux to True so that we always get back both stdout and stderr ret = self.container.exec_run(cmd, demux=True, **kwargs) exitcode = ret.exit_code stdout = stderr = None if ret.output: stdout, stderr = ret.output if stdout is not None: stdout = stdout.decode() if stderr is not None: stderr = stderr.decode() return ProcessResult(exitcode=exitcode, stdout=stdout, stderr=stderr, cmdline=cmd)
def test_cli_timeout_greater_than_timeout_kw(minion_id, config_dir, config_file, cli_script_name): # Both --timeout and _timeout are passed. # Since --timeout is greater than _timeout, the value of _timeout is updated to the value of --timeout plus 5 timeout = 10 explicit_timeout = 20 cli_timeout = 60 config = {"conf_file": config_file, "id": "the-id"} args = ["--timeout", str(cli_timeout), "test.ping"] kwargs = {"minion_tgt": minion_id, "_timeout": explicit_timeout} expected = [ sys.executable, cli_script_name, "--config-dir={}".format(config_dir), "--out=json", "--out-indent=0", "--log-level=critical", minion_id, "--timeout", "60", "test.ping", ] popen_mock = mock.MagicMock() popen_mock.pid = os.getpid() popen_mock.poll = mock.MagicMock(side_effect=[None, None, None, None, True]) terminate_mock = mock.MagicMock(return_value=ProcessResult(0, "", "", cmdline=())) popen_mock.terminate = terminate_mock proc = SaltCli(script_name=cli_script_name, config=config, timeout=timeout) with mock.patch.object(proc.impl, "init_terminal", popen_mock), mock.patch.object( proc, "terminate", terminate_mock ): proc.impl._terminal = popen_mock # We set __cli_timeout_supported__ to True just to test. This would be an attribute set # at the class level for Salt CLI's that support the timeout flag, like for example, salt-run proc.__cli_timeout_supported__ = True ret = proc.run(*args, **kwargs) assert proc.impl._terminal_timeout == cli_timeout + 10 assert popen_mock.call_args[0][0] == expected # pylint: disable=unsubscriptable-object
def vault_container_version(request, salt_call_cli, vault_port): vault_version = request.param container_started = False try: vault_binary = salt.utils.path.which("vault") config = { "backend": { "file": { "path": "/vault/file" } }, "default_lease_ttl": "168h", "max_lease_ttl": "720h", } ret = salt_call_cli.run("state.single", "docker_image.present", name="vault", tag=vault_version) assert ret.exitcode == 0 assert ret.json state_run = next(iter(ret.json.values())) assert state_run["result"] is True container_started = True attempts = 0 env = os.environ.copy() env["VAULT_ADDR"] = "http://127.0.0.1:{}".format(vault_port) while attempts < 3: attempts += 1 ret = salt_call_cli.run( "state.single", "docker_container.running", name="vault", image="vault:{}".format(vault_version), port_bindings="{}:8200".format(vault_port), environment={ "VAULT_DEV_ROOT_TOKEN_ID": "testsecret", "VAULT_LOCAL_CONFIG": json.dumps(config), }, cap_add="IPC_LOCK", ) assert ret.exitcode == 0 assert ret.json state_run = next(iter(ret.json.values())) assert state_run["result"] is True time.sleep(1) proc = subprocess.run( [vault_binary, "login", "token=testsecret"], env=env, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, ) if proc.returncode == 0: break ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) log.debug("Failed to authenticate against vault:\n%s", ret) time.sleep(4) else: pytest.fail("Failed to login to vault") proc = subprocess.run( [ vault_binary, "policy", "write", "testpolicy", "{}/vault.hcl".format(RUNTIME_VARS.FILES), ], env=env, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, ) if proc.returncode != 0: ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) log.debug("Failed to assign policy to vault:\n%s", ret) pytest.fail("unable to assign policy to vault") if vault_version == "1.3.1": proc = subprocess.run( [vault_binary, "secrets", "enable", "kv-v2"], env=env, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, ) ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) if proc.returncode != 0: log.debug("Failed to enable kv-v2:\n%s", ret) pytest.fail("Could not enable kv-v2") if "path is already in use at kv-v2/" in proc.stdout: pass elif "Success" in proc.stdout: pass else: log.debug("Failed to enable kv-v2:\n%s", ret) pytest.fail("Could not enable kv-v2 {}".format(proc.stdout)) yield vault_version finally: if container_started: ret = salt_call_cli.run("state.single", "docker_container.stopped", name="vault") assert ret.exitcode == 0 assert ret.json state_run = next(iter(ret.json.values())) assert state_run["result"] is True ret = salt_call_cli.run("state.single", "docker_container.absent", name="vault") assert ret.exitcode == 0 assert ret.json state_run = next(iter(ret.json.values())) assert state_run["result"] is True
def gpg_homedir(salt_master, pillar_state_tree): _gpg_homedir = pathlib.Path(salt_master.config_dir) / "gpgkeys" _gpg_homedir.mkdir(0o700) agent_started = False try: cmd_prefix = ["gpg", "--homedir", str(_gpg_homedir)] cmd = cmd_prefix + ["--list-keys"] proc = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, universal_newlines=True, ) ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) log.debug("Instantiating gpg keyring...\n%s", ret) cmd = cmd_prefix + ["--import", "--allow-secret-key-import"] proc = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, universal_newlines=True, input=TEST_KEY, ) ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) log.debug("Importing keypair...:\n%s", ret) agent_started = True top_file_contents = """ base: '*': - gpg """ with pytest.helpers.temp_file( "top.sls", top_file_contents, pillar_state_tree), pytest.helpers.temp_file( "gpg.sls", GPG_PILLAR_YAML, pillar_state_tree): yield _gpg_homedir finally: if agent_started: try: cmd = ["gpg-connect-agent", "--homedir", str(_gpg_homedir)] proc = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, universal_newlines=True, input="KILLAGENT", ) ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) log.debug("Killed gpg-agent...\n%s", ret) except (OSError, subprocess.CalledProcessError): log.debug( "No need to kill: old gnupg doesn't start the agent.") shutil.rmtree(str(_gpg_homedir), ignore_errors=True)
def test_default_cli_flags_with_timeout_and_timeout_kwargs( minion_id, config_dir, config_file, cli_script_name ): """ This test assures that when _timeout is passed as a keyword argument, that the _terminal_timeout property does not get get updated to the value of --timeout """ default_timeout = 10 explicit_timeout = 60 config = {"conf_file": config_file, "id": "the-id"} args = ["--timeout=6", "test.ping"] kwargs = {"minion_tgt": minion_id, "_timeout": explicit_timeout} expected = [ sys.executable, cli_script_name, "--config-dir={}".format(config_dir.strpath), "--out=json", "--log-level=quiet", minion_id, "--timeout=6", "test.ping", ] popen_mock = mock.MagicMock() popen_mock.pid = os.getpid() popen_mock.poll = mock.MagicMock(side_effect=[None, None, None, None, True]) terminate_mock = mock.MagicMock(return_value=ProcessResult(0, "", "", cmdline=())) popen_mock.terminate = terminate_mock proc = SaltCliFactory( cli_script_name=cli_script_name, config=config, default_timeout=default_timeout ) with mock.patch.object(proc, "init_terminal", popen_mock), mock.patch.object( proc, "terminate", terminate_mock ): proc._terminal = popen_mock # We set __cli_timeout_supported__ to True just to test. This would be an attribute set # at the class level for Salt CLI's that support the timeout flag, like for example, salt-run proc.__cli_timeout_supported__ = True ret = proc.run(*args, **kwargs) assert proc._terminal_timeout_set_explicitly is True assert proc._terminal_timeout == explicit_timeout assert popen_mock.call_args[0][0] == expected # pylint: disable=unsubscriptable-object # To confirm behavior, let's NOT pass --timeout in args default_timeout = 10 explicit_timeout = 60 config = {"conf_file": config_file, "id": "the-id"} args = ["test.ping"] kwargs = {"minion_tgt": minion_id, "_timeout": explicit_timeout} expected = [ sys.executable, cli_script_name, "--config-dir={}".format(config_dir.strpath), "--timeout={}".format(explicit_timeout), "--out=json", "--log-level=quiet", minion_id, "test.ping", ] popen_mock = mock.MagicMock() popen_mock.pid = os.getpid() popen_mock.poll = mock.MagicMock(side_effect=[None, None, None, None, True]) popen_mock.terminate = mock.MagicMock(return_value=ProcessResult(0, "", "", cmdline=())) terminate_mock = mock.MagicMock(return_value=ProcessResult(0, "", "")) proc = SaltCliFactory( cli_script_name=cli_script_name, config=config, default_timeout=default_timeout ) with mock.patch.object(proc, "init_terminal", popen_mock), mock.patch.object( proc, "terminate", terminate_mock ): proc._terminal = popen_mock # We set __cli_timeout_supported__ to True just to test. This would be an attribute set # at the class level for Salt CLI's that support the timeout flag, like for example, salt-run proc.__cli_timeout_supported__ = True ret = proc.run(*args, **kwargs) assert proc._terminal_timeout_set_explicitly is True assert proc._terminal_timeout == explicit_timeout assert popen_mock.call_args[0][0] == expected # pylint: disable=unsubscriptable-object # To confirm behavior, let's NOT pass --timeout in args nor _timeout in kwargs default_timeout = 10 config = {"conf_file": config_file, "id": "the-id"} args = ["test.ping"] kwargs = {"minion_tgt": minion_id} expected = [ sys.executable, cli_script_name, "--config-dir={}".format(config_dir.strpath), "--timeout={}".format(default_timeout - 5), "--out=json", "--log-level=quiet", minion_id, "test.ping", ] popen_mock = mock.MagicMock() popen_mock.pid = os.getpid() popen_mock.poll = mock.MagicMock(side_effect=[None, None, None, None, True]) popen_mock.terminate = mock.MagicMock(return_value=ProcessResult(0, "", "", cmdline=())) terminate_mock = mock.MagicMock(return_value=ProcessResult(0, "", "")) proc = SaltCliFactory( cli_script_name=cli_script_name, config=config, default_timeout=default_timeout ) with mock.patch.object(proc, "init_terminal", popen_mock), mock.patch.object( proc, "terminate", terminate_mock ): proc._terminal = popen_mock # We set __cli_timeout_supported__ to True just to test. This would be an attribute set # at the class level for Salt CLI's that support the timeout flag, like for example, salt-run proc.__cli_timeout_supported__ = True ret = proc.run(*args, **kwargs) assert proc._terminal_timeout_set_explicitly is False assert proc._terminal_timeout == default_timeout assert popen_mock.call_args[0][0] == expected # pylint: disable=unsubscriptable-object
def test_interrupt_on_long_running_job(salt_cli, salt_master, salt_minion): """ Ensure that a call to ``salt`` that is taking too long, when a user hits CTRL-C, that the JID is printed to the console. Refer to https://github.com/saltstack/salt/issues/60963 for more details """ # Ensure test.sleep is working as supposed start = time.time() ret = salt_cli.run("test.sleep", "1", minion_tgt=salt_minion.id) stop = time.time() assert ret.exitcode == 0 assert ret.json is True assert stop - start > 1, "The command should have taken more than 1 second" # Now the real test terminal_stdout = tempfile.SpooledTemporaryFile(512000, buffering=0) terminal_stderr = tempfile.SpooledTemporaryFile(512000, buffering=0) cmdline = [ sys.executable, salt_cli.get_script_path(), "--config-dir={}".format(salt_master.config_dir), salt_minion.id, "test.sleep", "30", ] # If this test starts failing, commend the following block of code proc = subprocess.Popen( cmdline, shell=False, stdout=terminal_stdout, stderr=terminal_stderr, universal_newlines=True, ) # and uncomment the following block of code # with default_signals(signal.SIGINT, signal.SIGTERM): # proc = subprocess.Popen( # cmdline, # shell=False, # stdout=terminal_stdout, # stderr=terminal_stderr, # universal_newlines=True, # ) # What this means is that something in salt or the test suite is setting # the SIGTERM and SIGINT signals to SIG_IGN, ignore. # Check which line of code is doing that and fix it start = time.time() try: # Make sure it actually starts proc.wait(1) except subprocess.TimeoutExpired: pass else: terminate_process(proc.pid, kill_children=True) pytest.fail("The test process failed to start") time.sleep(2) # Send CTRL-C to the process os.kill(proc.pid, signal.SIGINT) with proc: # Wait for the process to terminate, to avoid zombies. # Shouldn't really take the 30 seconds proc.wait(30) # poll the terminal so the right returncode is set on the popen object proc.poll() # This call shouldn't really be necessary proc.communicate() stop = time.time() terminal_stdout.flush() terminal_stdout.seek(0) if sys.version_info < (3, 6): # pragma: no cover stdout = proc._translate_newlines( terminal_stdout.read(), __salt_system_encoding__ ) else: stdout = proc._translate_newlines( terminal_stdout.read(), __salt_system_encoding__, sys.stdout.errors ) terminal_stdout.close() terminal_stderr.flush() terminal_stderr.seek(0) if sys.version_info < (3, 6): # pragma: no cover stderr = proc._translate_newlines( terminal_stderr.read(), __salt_system_encoding__ ) else: stderr = proc._translate_newlines( terminal_stderr.read(), __salt_system_encoding__, sys.stderr.errors ) terminal_stderr.close() ret = ProcessResult(proc.returncode, stdout, stderr, cmdline=proc.args) log.debug(ret) # If the minion ID is on stdout it means that the command finished and wasn't terminated assert ( salt_minion.id not in ret.stdout ), "The command wasn't actually terminated. Took {} seconds.".format( round(stop - start, 2) ) # Make sure the ctrl+c exited gracefully assert "Exiting gracefully on Ctrl-c" in ret.stderr assert "Exception ignored in" not in ret.stderr assert "This job's jid is" in ret.stderr
def gpg_homedir(salt_master, test_key): """ Setup gpg environment """ _gpg_homedir = pathlib.Path(salt_master.config_dir) / "gpgkeys" _gpg_homedir.mkdir(0o700) agent_started = False try: cmd_prefix = ["gpg", "--homedir", str(_gpg_homedir)] cmd = cmd_prefix + ["--list-keys"] proc = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, universal_newlines=True, ) ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) log.debug("Instantiating gpg keyring...\n%s", ret) cmd = cmd_prefix + ["--import", "--allow-secret-key-import"] proc = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, universal_newlines=True, input=test_key, ) ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) log.debug("Importing keypair...:\n%s", ret) agent_started = True yield _gpg_homedir finally: if agent_started: try: cmd = ["gpg-connect-agent", "--homedir", str(_gpg_homedir)] proc = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, universal_newlines=True, input="KILLAGENT", ) ret = ProcessResult( exitcode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr, cmdline=proc.args, ) log.debug("Killed gpg-agent...\n%s", ret) except (OSError, subprocess.CalledProcessError): log.debug("No need to kill: old gnupg doesn't start the agent.") shutil.rmtree(str(_gpg_homedir), ignore_errors=True)
def test_non_int_exitcode_raises_exception(exitcode): with pytest.raises(ValueError): ProcessResult(exitcode, None, None)