Ejemplo n.º 1
0
    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
Ejemplo n.º 3
0
 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
Ejemplo n.º 6
0
 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
Ejemplo n.º 7
0
    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 = []
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
 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)
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 14
0
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
Ejemplo n.º 15
0
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)