Example #1
0
    def _ansible(self,
                 *args,
                 utility=None,
                 check=True,
                 pipe_stdout=False,
                 **run_kwargs):
        """
        Call Ansible.

        Args:
            args: Ansible positional arguments.
            run_kwargs: subprocess
            check (bool): If True, Check return code for error.
            pipe_stdout (bool): If True, redirect stdout into a pipe, this allow
                to hide outputs from sys.stdout and permit to retrieve stdout as
                "result.stdout".

        Returns:
            subprocess.CompletedProcess: Ansible call result.
        """
        return call([
            executable,
            f"{self._executable()}-{utility}" if utility else self._executable
        ] + list(arg for arg in args if arg),
                    cwd=self._config_dir,
                    check=check,
                    pipe_stdout=pipe_stdout,
                    **run_kwargs)
Example #2
0
def yaml_lint(files, **kwargs):
    """
    Lint YAML files.

    Args:
        files (list of str): Paths to YAML files.
    """
    from accelpy._common import call
    return call(['yamllint', '-s'] + files, **kwargs)
Example #3
0
    def _get_executable(cls):
        """
        Get utility executable path after installing or updating it if
        required.
        """
        if not cls._executable:
            # Get utility release information from HashiCorp checkpoint API
            last_release = cls._get_last_version()

            # Check if executable is already installed and up-to-date
            cls._executable = exec_file = join(cls._install_dir(),
                                               last_release['executable_name'])

            if isfile(exec_file):
                line = call((exec_file, 'version'),
                            pipe_stdout=True).stdout.splitlines()[0]
                exec_version = line.split(' ')[1].strip().lstrip('v')

                # If file is installed and up-to-date, returns its path
                current_version = last_release['current_version']
                if exec_version == current_version:  # pragma: no branch
                    return exec_file

            # Download executables checksum file and associated signature
            checksum_raw = cls._download(last_release['checksum_url']).content
            checksum_sig_raw = cls._download(
                last_release['signature_url']).content

            # Verify checksum file signature against HashiCorp GPG key
            cls._gpg_verify(checksum_raw, checksum_sig_raw)

            # Download the latest compressed executable
            compressed = cls._download(last_release['archive_url']).content

            # Verify executable checksum
            cls._checksum_verify(checksum_raw, compressed,
                                 last_release['archive_name'])

            # Ensure directories exists
            makedirs(cls._install_dir(), exist_ok=True)

            # Lazy import package that are required only on install
            from io import BytesIO
            from zipfile import ZipFile

            # Extract executable and returns its path
            compressed_file_obj = BytesIO(compressed)
            compressed_file_obj.seek(0)
            with ZipFile(compressed_file_obj) as compressed_file:
                cls._executable = compressed_file.extract(
                    compressed_file.namelist()[0], path=cls._install_dir())

            # Ensure the file is executable
            chmod(cls._executable, stat(cls._executable).st_mode | 0o111)

        return cls._executable
Example #4
0
    def _gpg_verify(cls, data, signature):
        """
        Verify GPG signature with HashiCorp public key.

        Args:
            data (bytes): Data to verify.
            signature (bytes): Signature against verify data.

        Raises:
            accelpy.exceptions.RuntimeException: Invalid signature.

        References:
            https://www.hashicorp.com/security.html
        """
        # Import HashiCorp GPG public key
        call(
            ('gpg', '--import', join(dirname(__file__), 'gpg_public_key.asc')),
            pipe_stdout=True)

        # Lazy import package that are required only on install
        from tempfile import TemporaryDirectory

        # Verify signature
        with TemporaryDirectory() as tmp:

            data_path = join(tmp, 'data')
            with open(data_path, 'wb') as data_file:
                data_file.write(data)

            signature_path = join(tmp, 'data.sig')
            with open(signature_path, 'wb') as signature_file:
                signature_file.write(signature)

            gpg_valid = call(('gpg', '--verify', signature_path, data_path),
                             pipe_stdout=True,
                             check=False)

        if gpg_valid.returncode:
            raise RuntimeException(
                f'Unable to update {cls._name()}: Invalid signature')
Example #5
0
def test_call():
    """
    Test call
    """
    from subprocess import CompletedProcess, PIPE
    import accelpy._common as common
    from accelpy.exceptions import RuntimeException
    from accelpy._common import call

    # Mock subprocess
    retries = {}
    returncode = 0

    def run(*args, **kwargs):
        """Mocker run"""
        if retries:
            # Simulate failure to retry
            result = CompletedProcess(args, 1)
            retries.clear()

        else:
            result = CompletedProcess(args, returncode)

        result.kwargs = kwargs
        return result

    common_run = common._run
    common._run = run

    # Tests
    try:

        # Test: pipe_stdout
        assert 'stdout' not in call([]).kwargs
        assert call([], pipe_stdout=True).kwargs['stdout'] == PIPE

        # Test: Passing keyword arguments
        assert call([], testing=True).kwargs['testing'] is True

        # Test: retries
        retries[0] = 0
        assert call([], retries=3).returncode == returncode
        assert not retries

        # Test: check
        returncode = 1
        assert call([], check=False).returncode == returncode
        with pytest.raises(RuntimeException):
            call([], check=True)

    # Restore mocked functions
    finally:
        common._run = common_run
Example #6
0
def cli(*args):
    """
    Call cli

    Args:
        *args: CLI arguments.

    Returns:
        subprocess.CompletedProcess: Utility call result.
    """
    from sys import executable
    from accelpy._common import call
    from accelpy.__main__ import __file__ as cli_exec

    return call([executable, cli_exec] + [str(arg) for arg in args],
                pipe_stdout=True,
                check=False)
Example #7
0
    def _exec(self, *args, check=True, pipe_stdout=False, **run_kwargs):
        """
        Call utility.

        Args:
            args: Utility positional arguments.
            run_kwargs: subprocess
            check (bool): If True, Check return code for error.
            pipe_stdout (bool): If True, redirect stdout into a pipe, this allow
                to hide outputs from sys.stdout and permit to retrieve stdout as
                "result.stdout".

        Returns:
            subprocess.CompletedProcess: Utility call result.
        """
        return call([self._get_executable()] + [arg for arg in args if arg],
                    cwd=self._config_dir,
                    check=check,
                    pipe_stdout=pipe_stdout,
                    **run_kwargs)
Example #8
0
def cli(*args, **env):
    """
    Call cli

    Args:
        *args: CLI arguments.
        **env: Environment variables

    Returns:
        subprocess.CompletedProcess: Utility call result.
    """
    from os import environ
    from sys import executable
    from accelpy._common import call
    from accelpy.__main__ import __file__ as cli_exec

    call_env = environ.copy()
    call_env.update(env)

    return call([executable, cli_exec] + [str(arg) for arg in args],
                pipe_stdout=True, check=False, env=call_env)