示例#1
0
 def command(
     self,
     # Details for speaking to MySQL via `mysql` CLI via `mysql` CLI
     mysql_user=None,
     mysql_password=None,
     mysql_host=None,
     mysql_port=None,
 ):
     mysql_command = make_execute_mysql_command(
         self.mysql_command,
         user=mysql_user,
         password=mysql_password,
         host=mysql_host,
         port=mysql_port,
     )
     return StringCommand(
         'which',
         'mysql',
         '>',
         '/dev/null',
         '&&',
         StringCommand('(', mysql_command, ')', separator=''),
         '||',
         'true',
     )
示例#2
0
    def test_nested_deploy(self):
        inventory = make_inventory()
        somehost = inventory.get_host("somehost")

        state = State(inventory, Config())

        # Enable printing on this test to catch any exceptions in the formatting
        state.print_output = True
        state.print_input = True
        state.print_fact_info = True
        state.print_noop_info = True

        connect_all(state)

        @deploy
        def test_nested_deploy():
            server.shell(commands=["echo nested command"])

        @deploy
        def test_deploy():
            server.shell(commands=["echo first command"])
            test_nested_deploy()
            server.shell(commands=["echo second command"])

        add_deploy(state, test_deploy)

        op_order = state.get_op_order()

        # Ensure we have an op
        assert len(op_order) == 3

        first_op_hash = op_order[0]
        assert state.op_meta[first_op_hash]["names"] == {
            "test_deploy | Server/Shell"
        }
        assert state.ops[somehost][first_op_hash]["commands"] == [
            StringCommand("echo first command"),
        ]

        second_op_hash = op_order[1]
        assert state.op_meta[second_op_hash]["names"] == {
            "test_deploy | test_nested_deploy | Server/Shell",
        }
        assert state.ops[somehost][second_op_hash]["commands"] == [
            StringCommand("echo nested command"),
        ]

        third_op_hash = op_order[2]
        assert state.op_meta[third_op_hash]["names"] == {
            "test_deploy | Server/Shell"
        }
        assert state.ops[somehost][third_op_hash]["commands"] == [
            StringCommand("echo second command"),
        ]
示例#3
0
def _raise_or_remove_invalid_path(fs_type, path, force, force_backup,
                                  force_backup_dir):
    if force:
        if force_backup:
            backup_path = "{0}.{1}".format(path, get_timestamp())
            if force_backup_dir:
                backup_path = os.path.basename(backup_path)
                backup_path = "{0}/{1}".format(force_backup_dir, backup_path)
            yield StringCommand("mv", QuoteString(path),
                                QuoteString(backup_path))
        else:
            yield StringCommand("rm", "-rf", QuoteString(path))
    else:
        raise OperationError("{0} exists and is not a {1}".format(
            path, fs_type))
示例#4
0
def load(
    state, host,
    remote_filename, database=None,
    # Details for speaking to PostgreSQL via `psql` CLI
    postgresql_user=None, postgresql_password=None,
    postgresql_host=None, postgresql_port=None,
):
    '''
    Load ``.sql`` file into a database.

    + database: name of the database to import into
    + remote_filename: the filename to read from
    + postgresql_*: global module arguments, see above

    Example:

    .. code:: python

        postgresql.load(
            {'Import the pyinfra_stuff dump into pyinfra_stuff_copy'},
            '/tmp/pyinfra_stuff.dump',
            database='pyinfra_stuff_copy',
            sudo_user='******',
        )

    '''

    yield StringCommand(make_psql_command(
        database=database,
        user=postgresql_user,
        password=postgresql_password,
        host=postgresql_host,
        port=postgresql_port,
    ), '<', remote_filename)
示例#5
0
def make_mysql_command(
    database=None,
    user=None,
    password=None,
    host=None,
    port=None,
    executable="mysql",
):
    target_bits = [executable]

    if database:
        target_bits.append(database)

    if user:
        # Quote the username as in may contain special characters
        target_bits.append('-u"{0}"'.format(user))

    if password:
        # Quote the password as it may contain special characters
        target_bits.append(MaskString('-p"{0}"'.format(password)))

    if host:
        target_bits.append("-h{0}".format(host))

    if port:
        target_bits.append("-P{0}".format(port))

    return StringCommand(*target_bits)
示例#6
0
def dump(
    state, host,
    remote_filename, database=None,
    # Details for speaking to PostgreSQL via `psql` CLI
    postgresql_user=None, postgresql_password=None,
    postgresql_host=None, postgresql_port=None,
):
    '''
    Dump a PostgreSQL database into a ``.sql`` file. Requires ``pg_dump``.

    + database: name of the database to dump
    + remote_filename: name of the file to dump the SQL to
    + postgresql_*: global module arguments, see above

    Example:

    .. code:: python

        postgresql.dump(
            {'Dump the pyinfra_stuff database'},
            '/tmp/pyinfra_stuff.dump',
            database='pyinfra_stuff',
            sudo_user='******',
        )

    '''

    yield StringCommand(make_psql_command(
        executable='pg_dump',
        database=database,
        user=postgresql_user,
        password=postgresql_password,
        host=postgresql_host,
        port=postgresql_port,
    ), '>', remote_filename)
示例#7
0
    def test_run_shell_command_masked(self, fake_click):
        inventory = make_inventory(hosts=('@local', ))
        state = State(inventory, Config())
        host = inventory.get_host('@local')

        command = StringCommand('echo', MaskString('top-secret-stuff'))
        self.fake_popen_mock().returncode = 0

        out = host.run_shell_command(state,
                                     command,
                                     print_output=True,
                                     print_input=True)
        assert len(out) == 3

        status, stdout, stderr = out
        assert status is True

        self.fake_popen_mock.assert_called_with(
            "sh -c 'echo top-secret-stuff'",
            shell=True,
            stdout=PIPE,
            stderr=PIPE,
            stdin=PIPE,
        )

        fake_click.echo.assert_called_with(
            "{0}>>> sh -c 'echo ***'".format(host.print_prefix), )
示例#8
0
def make_psql_command(
    database=None,
    user=None,
    password=None,
    host=None,
    port=None,
    executable='psql',
):
    target_bits = []

    if password:
        target_bits.append(MaskString('PGPASSWORD="******"'.format(password)))

    target_bits.append(executable)

    if database:
        target_bits.append('-d {0}'.format(database))

    if user:
        target_bits.append('-U {0}'.format(user))

    if host:
        target_bits.append('-h {0}'.format(host))

    if port:
        target_bits.append('-p {0}'.format(port))

    return StringCommand(*target_bits)
示例#9
0
def make_execute_psql_command(command, **postgresql_kwargs):
    return StringCommand(
        make_psql_command(**postgresql_kwargs),
        '-Ac',
        QuoteString(
            command),  # quote this whole item as a single shell argument
    )
示例#10
0
    def test_run_shell_command_masked(self, fake_ssh_client, fake_click):
        fake_ssh = MagicMock()
        fake_stdout = MagicMock()
        fake_ssh.exec_command.return_value = MagicMock(
        ), fake_stdout, MagicMock()

        fake_ssh_client.return_value = fake_ssh

        inventory = make_inventory(hosts=("somehost", ))
        State(inventory, Config())
        host = inventory.get_host("somehost")
        host.connect()

        command = StringCommand("echo", MaskString("top-secret-stuff"))
        fake_stdout.channel.recv_exit_status.return_value = 0

        out = host.run_shell_command(command,
                                     print_output=True,
                                     print_input=True)
        assert len(out) == 3

        status, stdout, stderr = out
        assert status is True

        fake_ssh.exec_command.assert_called_with(
            "sh -c 'echo top-secret-stuff'",
            get_pty=False,
        )

        fake_click.echo.assert_called_with(
            "{0}>>> sh -c 'echo ***'".format(host.print_prefix),
            err=True,
        )
示例#11
0
def update(
    state: Optional[State] = None,
    host: Optional[Host] = None
) -> Generator[StringCommand, None, None]:  # noqa
    """Upgrade system via PackageKit console client."""
    yield StringCommand("pkcon update --plain --noninteractive",
                        success_exit_codes=[0, 5])
示例#12
0
def chmod(target, mode, recursive=False):
    args = ["chmod"]
    if recursive:
        args.append("-R")

    args.append("{0}".format(mode))

    return StringCommand(" ".join(args), QuoteString(target))
示例#13
0
def parse_commands(commands):
    json_commands = []

    for command in commands:
        if isinstance(command,
                      six.string_types):  # matches pyinfra/api/operation.py
            command = StringCommand(command.strip())

        if isinstance(command, StringCommand):
            json_command = get_command_string(command)

        elif isinstance(command, dict):
            command['command'] = get_command_string(command['command']).strip()
            json_command = command

        elif isinstance(command, FunctionCommand):
            func_name = (command.function if command.function == '__func__'
                         else command.function.__name__)
            json_command = [
                func_name,
                list(command.args),
                command.kwargs,
            ]

        elif isinstance(command, FileUploadCommand):
            if hasattr(command.src, 'read'):
                command.src.seek(0)
                data = command.src.read()
            else:
                data = command.src
            json_command = ['upload', data, command.dest]

        elif isinstance(command, FileDownloadCommand):
            json_command = ['download', command.src, command.dest]

        else:
            raise Exception('{0} is not a valid command!'.format(command))

        if command.executor_kwargs:
            command.executor_kwargs['command'] = json_command
            json_command = command.executor_kwargs

        json_commands.append(json_command)
    return json_commands
示例#14
0
def parse_commands(commands):
    json_commands = []

    for command in commands:
        if isinstance(command, str):  # matches pyinfra/api/operation.py
            command = StringCommand(command.strip())

        if isinstance(command, StringCommand):
            json_command = get_command_string(command)

        elif isinstance(command, dict):
            command["command"] = get_command_string(command["command"]).strip()
            json_command = command

        elif isinstance(command, FunctionCommand):
            func_name = (command.function if command.function == "__func__"
                         else command.function.__name__)
            json_command = [
                func_name,
                list(command.args),
                command.kwargs,
            ]

        elif isinstance(command, FileUploadCommand):
            if hasattr(command.src, "read"):
                command.src.seek(0)
                data = command.src.read()
            else:
                data = str(command.src)
            json_command = ["upload", data, str(command.dest)]

        elif isinstance(command, FileDownloadCommand):
            json_command = ["download", str(command.src), str(command.dest)]

        else:
            raise Exception("{0} is not a valid command!".format(command))

        if command.executor_kwargs:
            command.executor_kwargs["command"] = json_command
            json_command = command.executor_kwargs

        json_commands.append(json_command)
    return json_commands
示例#15
0
def make_execute_mysql_command(command, ignore_errors=False, **mysql_kwargs):
    commands_bits = [
        make_mysql_command(**mysql_kwargs),
        "-Be",
        QuoteString(command),  # quote this whole item as a single shell argument
    ]

    if ignore_errors:
        commands_bits.extend(["||", "true"])

    return StringCommand(*commands_bits)
示例#16
0
        def jsontest_function(self, test_name, test_data, fact=fact):
            short_fact = None

            if isinstance(fact, ShortFactBase):
                short_fact = fact
                fact = fact.fact()

            test_args = test_data.get("arg", [])
            command = _make_command(fact.command, test_args)

            if "command" in test_data:
                assert get_command_string(
                    StringCommand(command)) == test_data["command"]
            else:
                warnings.warn(
                    'No command set for test: {0} (got "{1}")'.format(
                        test_name,
                        command,
                    ), )

            requires_command = _make_command(fact.requires_command, test_args)

            if requires_command:
                if "requires_command" in test_data:
                    assert requires_command == test_data["requires_command"]
                else:
                    warnings.warn(
                        'No requires command set for test: {0} (got "{1}")'.
                        format(
                            test_name,
                            requires_command,
                        ), )

            data = fact.process(test_data["output"])
            if short_fact:
                data = short_fact.process_data(data)

            # Encode/decode data to ensure datetimes/etc become JSON
            data = json.loads(json.dumps(data, default=json_encode))
            try:
                assert data == test_data["fact"]
            except AssertionError as e:
                print()
                print("--> GOT:\n",
                      json.dumps(data, indent=4, default=json_encode))
                print(
                    "--> WANT:",
                    json.dumps(
                        test_data["fact"],
                        indent=4,
                        default=json_encode,
                    ),
                )
                raise e
示例#17
0
def sed_replace(
    filename,
    line,
    replace,
    flags=None,
    backup=False,
    interpolate_variables=False,
):
    flags = "".join(flags) if flags else ""

    line = line.replace("/", r"\/")
    replace = str(replace)
    replace = replace.replace("/", r"\/")
    backup_extension = get_timestamp()

    if interpolate_variables:
        line = line.replace('"', '\\"')
        replace = replace.replace('"', '\\"')
        sed_script_formatter = '"s/{0}/{1}/{2}"'
    else:
        # Single quotes cannot contain other single quotes, even when escaped , so turn
        # each ' into '"'"' (end string, double quote the single quote, (re)start string)
        line = line.replace("'", "'\"'\"'")
        replace = replace.replace("'", "'\"'\"'")
        sed_script_formatter = "'s/{0}/{1}/{2}'"

    sed_script = sed_script_formatter.format(line, replace, flags)

    sed_command = StringCommand(
        "sed",
        "-i.{0}".format(backup_extension),
        sed_script,
        QuoteString(filename),
    )

    if not backup:  # if we're not backing up, remove the file *if* sed succeeds
        backup_filename = "{0}.{1}".format(filename, backup_extension)
        sed_command = StringCommand(sed_command, "&&", "rm", "-f",
                                    QuoteString(backup_filename))

    return sed_command
示例#18
0
def run_shell_command(state,
                      host,
                      command,
                      get_pty=False,
                      timeout=None,
                      stdin=None,
                      success_exit_codes=None,
                      print_output=False,
                      print_input=False,
                      return_combined_output=False,
                      use_sudo_password=False,
                      **command_kwargs):

    if use_sudo_password:
        command_kwargs['use_sudo_password'] = get_sudo_password(
            state,
            host,
            use_sudo_password,
            run_shell_command=run_shell_command,
            put_file=put_file,
        )

    chroot_directory = host.host_data['chroot_directory']

    command = make_unix_command(command, **command_kwargs)
    command = QuoteString(command)

    logger.debug(
        '--> Running chroot command on ({0}):{1}'.format(
            chroot_directory,
            command,
        ), )

    chroot_command = StringCommand(
        'chroot',
        chroot_directory,
        'sh',
        '-c',
        command,
    )

    return run_local_shell_command(
        state,
        host,
        chroot_command,
        timeout=timeout,
        stdin=stdin,
        success_exit_codes=success_exit_codes,
        print_output=print_output,
        print_input=print_input,
        return_combined_output=return_combined_output,
    )
示例#19
0
def reboot(delay=10, interval=1, reboot_timeout=300):
    """
    Reboot the server and wait for reconnection.

    + delay: number of seconds to wait before attempting reconnect
    + interval: interval (s) between reconnect attempts
    + reboot_timeout: total time before giving up reconnecting

    **Example:**

    .. code:: python

        server.reboot(
            name="Reboot the server and wait to reconnect",
            delay=60,
            reboot_timeout=600,
        )
    """

    # Remove this now, before we reboot the server - if the reboot fails (expected or
    # not) we'll error if we don't clean this up now. Will simply be re-uploaded if
    # needed later.
    def remove_any_askpass_file(state, host):
        remove_any_sudo_askpass_file(host)

    yield FunctionCommand(remove_any_askpass_file, (), {})

    yield StringCommand("reboot",
                        success_exit_codes=[0,
                                            -1])  # -1 being error/disconnected

    def wait_and_reconnect(state, host):  # pragma: no cover
        sleep(delay)
        max_retries = round(reboot_timeout / interval)

        host.connection = None  # remove the connection object
        retries = 0

        while True:
            host.connect(show_errors=False)
            if host.connection:
                break

            if retries > max_retries:
                raise Exception(
                    ("Server did not reboot in time (reboot_timeout={0}s)"
                     ).format(reboot_timeout), )

            sleep(interval)
            retries += 1

    yield FunctionCommand(wait_and_reconnect, (), {})
示例#20
0
    def command(self, command):
        bits = ["certbot", command]

        if self.cert_name:
            bits.append("--cert-name {0}".format(self.cert_name))

        if self.dns_provider:
            # create dns flag like, --dns-google
            bits.append("--dns-{0}".format(self.dns_provider))

        if self._domains:
            bits.append("-d {0}".format(self.domains))

        return StringCommand(*bits)
示例#21
0
def sed_replace(
    filename,
    line,
    replace,
    flags=None,
    backup=False,
    interpolate_variables=False,
):
    flags = ''.join(flags) if flags else ''

    line = line.replace('/', r'\/')
    replace = str(replace)
    replace = replace.replace('/', r'\/')
    backup_extension = get_timestamp()

    if interpolate_variables:
        line = line.replace('"', '\\"')
        replace = replace.replace('"', '\\"')
        sed_script_formatter = '"s/{0}/{1}/{2}"'
    else:
        # Single quotes cannot contain other single quotes, even when escaped , so turn
        # each ' into '"'"' (end string, double quote the single quote, (re)start string)
        line = line.replace("'", "'\"'\"'")
        replace = replace.replace("'", "'\"'\"'")
        sed_script_formatter = "'s/{0}/{1}/{2}'"

    sed_script = sed_script_formatter.format(line, replace, flags)

    sed_command = StringCommand('sed', '-i.{0}'.format(backup_extension),
                                sed_script, filename)

    if not backup:  # if we're not backing up, remove the file *if* sed succeeds
        backup_filename = '{0}.{1}'.format(filename, backup_extension)
        sed_command = StringCommand(sed_command, '&&', 'rm', '-f',
                                    backup_filename)

    return sed_command
示例#22
0
 def command(
     self,
     postgresql_user=None,
     postgresql_password=None,
     postgresql_host=None,
     postgresql_port=None,
 ):
     psql_command = make_execute_psql_command(
         self.postgresql_command,
         user=postgresql_user,
         password=postgresql_password,
         host=postgresql_host,
         port=postgresql_port,
     )
     return StringCommand(psql_command, '||', 'true')
示例#23
0
 def command(
     self,
     postgresql_user=None,
     postgresql_password=None,
     postgresql_host=None,
     postgresql_port=None,
 ):
     psql_command = make_execute_psql_command(
         self.postgresql_command,
         user=postgresql_user,
         password=postgresql_password,
         host=postgresql_host,
         port=postgresql_port,
     )
     return StringCommand(
         'which',
         'psql',
         '>',
         '/dev/null',
         '&&',
         StringCommand('(', psql_command, ')', separator=''),
         '||',
         'true',
     )
示例#24
0
文件: mysql.py 项目: vindarel/pyinfra
 def command(
     self,
     # Details for speaking to MySQL via `mysql` CLI via `mysql` CLI
     mysql_user=None,
     mysql_password=None,
     mysql_host=None,
     mysql_port=None,
 ):
     mysql_command = make_execute_mysql_command(
         self.mysql_command,
         user=mysql_user,
         password=mysql_password,
         host=mysql_host,
         port=mysql_port,
     )
     return StringCommand(mysql_command, '||', 'true')
示例#25
0
def run_shell_command(
    state,
    host,
    command,
    get_pty=False,
    timeout=None,
    stdin=None,
    success_exit_codes=None,
    print_output=False,
    print_input=False,
    return_combined_output=False,
    **command_kwargs,
):
    container_id = host.host_data["docker_container_id"]

    # Don't sudo/su in Docker - is this the right thing to do? Makes deploys that
    # target SSH systems work w/Docker out of the box (ie most docker commands
    # are run as root).
    for key in ("sudo", "su_user"):
        command_kwargs.pop(key, None)

    command = make_unix_command_for_host(state, host, command,
                                         **command_kwargs)
    command = QuoteString(command)

    docker_flags = "-it" if get_pty else "-i"
    docker_command = StringCommand(
        "docker",
        "exec",
        docker_flags,
        container_id,
        "sh",
        "-c",
        command,
    )

    return ssh.run_shell_command(
        state,
        host,
        docker_command,
        timeout=timeout,
        stdin=stdin,
        success_exit_codes=success_exit_codes,
        print_output=print_output,
        print_input=print_input,
        return_combined_output=return_combined_output,
    )
示例#26
0
def reboot(delay=10, interval=1, reboot_timeout=300, state=None, host=None):
    '''
    Reboot the server and wait for reconnection.

    + delay: number of seconds to wait before attempting reconnect
    + interval: interval (s) between reconnect attempts
    + reboot_timeout: total time before giving up reconnecting

    Note: Probably want sudo enabled.

    Example:

    .. code:: python

        server.reboot(
            name='Reboot the server and wait to reconnect',
            delay=5,
            timeout=30,
        )
    '''

    logger.warning('The server.reboot operation is in beta!')

    yield StringCommand('reboot',
                        success_exit_codes=[-1])  # -1 being error/disconnected

    def wait_and_reconnect(state, host):  # pragma: no cover
        sleep(delay)
        max_retries = round(reboot_timeout / interval)

        host.connection = None  # remove the connection object
        retries = 0

        while True:
            host.connect(state, show_errors=False)
            if host.connection:
                break

            if retries > max_retries:
                raise Exception(
                    ('Server did not reboot in time (reboot_timeout={0}s)'
                     ).format(reboot_timeout))

            sleep(interval)
            retries += 1

    yield FunctionCommand(wait_and_reconnect, (), {})
示例#27
0
        def jsontest_function(self, test_name, test_data, fact=fact):
            short_fact = None

            if isinstance(fact, ShortFactBase):
                short_fact = fact
                fact = fact.fact()

            command_to_check = None

            if callable(fact.command):
                args = test_data.get('arg', [])
                if not isinstance(args, list):
                    args = [args]

                command = fact.command(*args)

                if args or 'command' in test_data:
                    command_to_check = command

            elif 'command' in test_data:
                command_to_check = fact.command

            if command_to_check:
                assert get_command_string(
                    StringCommand(command_to_check)) == test_data['command']

            data = fact.process(test_data['output'])
            if short_fact:
                data = short_fact.process_data(data)

            # Encode/decode data to ensure datetimes/etc become JSON
            data = json.loads(json.dumps(data, default=json_encode))
            try:
                assert data == test_data['fact']
            except AssertionError as e:
                print()
                print('--> GOT:\n',
                      json.dumps(data, indent=4, default=json_encode))
                print(
                    '--> WANT:',
                    json.dumps(
                        test_data['fact'],
                        indent=4,
                        default=json_encode,
                    ))
                raise e
示例#28
0
def run_shell_command(state,
                      host,
                      command,
                      get_pty=False,
                      timeout=None,
                      stdin=None,
                      success_exit_codes=None,
                      print_output=False,
                      print_input=False,
                      return_combined_output=False,
                      **command_kwargs):
    container_id = host.host_data['docker_container_id']

    # Don't sudo/su in Docker - is this the right thing to do? Makes deploys that
    # target SSH systems work w/Docker out of the box (ie most docker commands
    # are run as root).
    for key in ('sudo', 'su_user'):
        command_kwargs.pop(key, None)

    command = make_unix_command(command, **command_kwargs)
    command = QuoteString(command)

    docker_flags = '-it' if get_pty else '-i'
    docker_command = StringCommand(
        'docker',
        'exec',
        docker_flags,
        container_id,
        'sh',
        '-c',
        command,
    )

    return ssh.run_shell_command(
        state,
        host,
        docker_command,
        timeout=timeout,
        stdin=stdin,
        success_exit_codes=success_exit_codes,
        print_output=print_output,
        print_input=print_input,
        return_combined_output=return_combined_output,
    )
示例#29
0
def run_shell_command(
    state,
    host,
    command,
    get_pty=False,
    timeout=None,
    stdin=None,
    success_exit_codes=None,
    print_output=False,
    print_input=False,
    return_combined_output=False,
    **command_kwargs,
):
    chroot_directory = host.connector_data["chroot_directory"]

    command = make_unix_command_for_host(state, host, command,
                                         **command_kwargs)
    command = QuoteString(command)

    logger.debug("--> Running chroot command on (%s): %s", chroot_directory,
                 command)

    chroot_command = StringCommand(
        "chroot",
        chroot_directory,
        "sh",
        "-c",
        command,
    )

    return run_local_shell_command(
        state,
        host,
        chroot_command,
        timeout=timeout,
        stdin=stdin,
        success_exit_codes=success_exit_codes,
        print_output=print_output,
        print_input=print_input,
        return_combined_output=return_combined_output,
    )
示例#30
0
def dump(
    dest,
    database=None,
    # Details for speaking to PostgreSQL via `psql` CLI
    psql_user=None,
    psql_password=None,
    psql_host=None,
    psql_port=None,
):
    """
    Dump a PostgreSQL database into a ``.sql`` file. Requires ``pg_dump``.

    + dest: name of the file to dump the SQL to
    + database: name of the database to dump
    + psql_*: global module arguments, see above

    **Example:**

    .. code:: python

        postgresql.dump(
            name="Dump the pyinfra_stuff database",
            dest="/tmp/pyinfra_stuff.dump",
            database="pyinfra_stuff",
            sudo_user="******",
        )

    """

    yield StringCommand(
        make_psql_command(
            executable="pg_dump",
            database=database,
            user=psql_user,
            password=psql_password,
            host=psql_host,
            port=psql_port,
        ),
        ">",
        dest,
    )