dycw-postgres 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.3
2
+ Name: dycw-postgres
3
+ Version: 0.2.0
4
+ Summary: Library to operate `postgres` and `pgbackrest`
5
+ Author: Derek Wan
6
+ Author-email: Derek Wan <d.wan@icloud.com>
7
+ Requires-Dist: click>=8.3.1
8
+ Requires-Dist: dycw-utilities>=0.189.1
9
+ Requires-Dist: click==8.3.1 ; extra == 'cli'
10
+ Requires-Dist: dycw-utilities==0.189.1 ; extra == 'cli'
11
+ Requires-Python: >=3.12
12
+ Provides-Extra: cli
13
+ Description-Content-Type: text/markdown
14
+
15
+ # `postgres`
16
+
17
+ Library to operate `postgres` and `pgbackrest`
@@ -0,0 +1,20 @@
1
+ postgres/__init__.py,sha256=N_crolGv2j6N_FPy_bbFMnZY5zHKh-OfDj3bEJIIGOo,707
2
+ postgres/_cli.py,sha256=K7ugaykYszy9SzQfYC08VCj5B7N-p1fQ83qs0clKPe8,1427
3
+ postgres/_click.py,sha256=fy1wPvt9Qj8pwkS7CaoWH0W2nCxFjfQ-o9vANb2vXiQ,1396
4
+ postgres/_constants.py,sha256=8fNn5L0BLmjshmiKz2sncf_xiWvbchNOPG5jNw3MITg,91
5
+ postgres/_enums.py,sha256=s7Bli5hyBef2mufS-M5uJmRSp9oRmw87aqb5Spabg78,561
6
+ postgres/_types.py,sha256=2b7Ys9QuOOxvkP_9-xFu4LjcbBw6r9PeKxSOoxgxVhQ,79
7
+ postgres/_utilities.py,sha256=xhXKaXvtiB8_2w_Bz2cqwJnHREyyNglAoYPpoDNBFHY,2233
8
+ postgres/commands/__init__.py,sha256=ERcOSbBeTHWQmIjZ9jv6Hf25OqrwsC-q2wmpzdPagHo,754
9
+ postgres/commands/_backup.py,sha256=Rgen0KLjZLvYriQVPliVqIrV4caasS8QT_PlWy0gIVU,2682
10
+ postgres/commands/_check.py,sha256=jccA2IxnCvPUvqRvA8_oAojBChgIOEx4buMN45dkcPA,1301
11
+ postgres/commands/_info.py,sha256=kfKzNyNLfpgebobncTzsbkzDXuO-ayBVW8DNjfqdI5k,1907
12
+ postgres/commands/_restore.py,sha256=WttJujZG2CgtaZxPJr_52HiAfCdZPQnxAB8TBsjTpqk,4056
13
+ postgres/commands/_stanza_create.py,sha256=lBOr-hoEOd2X0f7i-OpgEWiUqEPdn-2obn18ZBWQnUY,1323
14
+ postgres/commands/_start.py,sha256=J2_Tku1EwFHkGirOH_DAUYqrIrWLEVOmbn58UyGmwxE,1344
15
+ postgres/commands/_stop.py,sha256=kMN78T6v4wjA_5NCZWN95wZfTVsnGEOul0CGGcTw4ao,1343
16
+ postgres/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ dycw_postgres-0.2.0.dist-info/WHEEL,sha256=QnGI6l9Psotmz6XrseXTYT5jnZRJeTk4SwJP4aLtfdI,80
18
+ dycw_postgres-0.2.0.dist-info/entry_points.txt,sha256=NBa0-dhZS57irzZehip3Hh6fHyHwtD89AArmSol0a4k,300
19
+ dycw_postgres-0.2.0.dist-info/METADATA,sha256=9x6Vgj9TrRJKtjQqs2cOHW-rTLIlzJgTYuyTovM4w3Q,488
20
+ dycw_postgres-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.10.2
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,10 @@
1
+ [console_scripts]
2
+ backup = postgres._cli:backup_cli
3
+ check = postgres._cli:check_cli
4
+ info = postgres._cli:info_cli
5
+ postgres-cli = postgres._cli:group_cli
6
+ restore = postgres._cli:restore_cli
7
+ stanza-create = postgres._cli:stanza_create_cli
8
+ start = postgres._cli:start_cli
9
+ stop = postgres._cli:stop_cli
10
+
postgres/__init__.py ADDED
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from postgres._click import (
4
+ ClickRepo,
5
+ stanza_argument,
6
+ stanza_option,
7
+ type_default_option,
8
+ type_no_default_option,
9
+ user_option,
10
+ version_option,
11
+ )
12
+ from postgres._constants import POSTGRES_VERSION
13
+ from postgres._enums import DEFAULT_TYPE, Type
14
+ from postgres._types import Repo
15
+ from postgres._utilities import run_or_as_user, to_repo_num
16
+
17
+ __all__ = [
18
+ "DEFAULT_TYPE",
19
+ "POSTGRES_VERSION",
20
+ "ClickRepo",
21
+ "Repo",
22
+ "Type",
23
+ "run_or_as_user",
24
+ "stanza_argument",
25
+ "stanza_option",
26
+ "to_repo_num",
27
+ "type_default_option",
28
+ "type_no_default_option",
29
+ "user_option",
30
+ "version_option",
31
+ ]
32
+ __version__ = "0.2.0"
postgres/_cli.py ADDED
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ from click import group, version_option
4
+ from utilities.click import CONTEXT_SETTINGS
5
+
6
+ from postgres import __version__
7
+ from postgres.commands._backup import make_backup_cmd
8
+ from postgres.commands._check import make_check_cmd
9
+ from postgres.commands._info import make_info_cmd
10
+ from postgres.commands._restore import make_restore_cmd
11
+ from postgres.commands._stanza_create import make_stanza_create_cmd
12
+ from postgres.commands._start import make_start_cmd
13
+ from postgres.commands._stop import make_stop_cmd
14
+
15
+ backup_cli = make_backup_cmd()
16
+ check_cli = make_check_cmd()
17
+ info_cli = make_info_cmd()
18
+ stanza_create_cli = make_stanza_create_cmd()
19
+ restore_cli = make_restore_cmd()
20
+ start_cli = make_start_cmd()
21
+ stop_cli = make_stop_cmd()
22
+
23
+
24
+ @group(**CONTEXT_SETTINGS)
25
+ @version_option(version=__version__)
26
+ def group_cli() -> None: ...
27
+
28
+
29
+ _ = make_backup_cmd(cli=group_cli.command, name="backup")
30
+ _ = make_check_cmd(cli=group_cli.command, name="check")
31
+ _ = make_info_cmd(cli=group_cli.command, name="info")
32
+ _ = make_restore_cmd(cli=group_cli.command, name="restore")
33
+ _ = make_stanza_create_cmd(cli=group_cli.command, name="stanza-create")
34
+ _ = make_start_cmd(cli=group_cli.command, name="start")
35
+ _ = make_stop_cmd(cli=group_cli.command, name="stop")
36
+
37
+
38
+ __all__ = [
39
+ "backup_cli",
40
+ "check_cli",
41
+ "group_cli",
42
+ "info_cli",
43
+ "restore_cli",
44
+ "stanza_create_cli",
45
+ "start_cli",
46
+ "stop_cli",
47
+ ]
postgres/_click.py ADDED
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, override
4
+
5
+ from click import Context, Parameter, ParamType
6
+ from utilities.click import Enum, Str, argument, option
7
+
8
+ from postgres._constants import POSTGRES_VERSION
9
+ from postgres._enums import DEFAULT_TYPE, Type
10
+
11
+ if TYPE_CHECKING:
12
+ from postgres._types import Repo
13
+
14
+ # parameters
15
+
16
+
17
+ class ClickRepo(ParamType):
18
+ name = "repo"
19
+
20
+ @override
21
+ def __repr__(self) -> str:
22
+ return self.name.upper()
23
+
24
+ @override
25
+ def convert(
26
+ self, value: Repo, param: Parameter | None, ctx: Context | None
27
+ ) -> Repo:
28
+ _ = (param, ctx)
29
+ return value
30
+
31
+
32
+ # options
33
+
34
+
35
+ stanza_argument = argument("stanza", type=Str())
36
+ stanza_option = option("--stanza", type=Str(), default=None, help="Stanza name")
37
+ repo_option = option("--repo", type=ClickRepo(), default=None, help="Repo number/name")
38
+ type_default_option, type_no_default_option = [
39
+ option("--type", "type_", type=Enum(Type), default=d, help="Backup type")
40
+ for d in [DEFAULT_TYPE, None]
41
+ ]
42
+ user_option = option("--user", type=Str(), default=None, help="User to run as")
43
+ version_option = option(
44
+ "--version", type=int, default=POSTGRES_VERSION, help="Postgres version"
45
+ )
46
+
47
+
48
+ __all__ = [
49
+ "ClickRepo",
50
+ "stanza_argument",
51
+ "stanza_option",
52
+ "type_default_option",
53
+ "type_no_default_option",
54
+ "user_option",
55
+ "version_option",
56
+ ]
postgres/_constants.py ADDED
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ POSTGRES_VERSION = 17
4
+
5
+
6
+ __all__ = ["POSTGRES_VERSION"]
postgres/_enums.py ADDED
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import StrEnum, unique
4
+ from typing import assert_never
5
+
6
+
7
+ @unique
8
+ class Type(StrEnum):
9
+ full = "full"
10
+ diff = "diff"
11
+ incr = "incr"
12
+
13
+ @property
14
+ def desc(self) -> str:
15
+ match self:
16
+ case Type.full:
17
+ return "full"
18
+ case Type.diff:
19
+ return "differential"
20
+ case Type.incr:
21
+ return "incremental"
22
+ case never:
23
+ assert_never(never)
24
+
25
+
26
+ DEFAULT_TYPE = Type.incr
27
+
28
+
29
+ __all__ = ["DEFAULT_TYPE", "Type"]
postgres/_types.py ADDED
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ type Repo = str | int
4
+
5
+
6
+ __all__ = ["Repo"]
postgres/_utilities.py ADDED
@@ -0,0 +1,89 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Mapping
4
+ from shlex import join
5
+ from typing import TYPE_CHECKING, assert_never
6
+
7
+ from utilities.subprocess import run
8
+
9
+ if TYPE_CHECKING:
10
+ from utilities.types import LoggerLike, PathLike, Retry, StrStrMapping
11
+
12
+ from postgres._types import Repo
13
+
14
+
15
+ def to_repo_num(
16
+ *, repo: Repo | None = None, mapping: Mapping[str, int] | None = None
17
+ ) -> int:
18
+ """Convert a repo number/name to a number."""
19
+ match repo, mapping:
20
+ case int(), _:
21
+ return repo
22
+ case None, _:
23
+ return 1
24
+ case str(), Mapping():
25
+ return mapping[repo]
26
+ case str(), None:
27
+ msg = f"Repo {repo!r} is a string, but mappings were not provided"
28
+ raise ValueError(msg)
29
+ case never:
30
+ assert_never(never)
31
+
32
+
33
+ ##
34
+
35
+
36
+ def run_or_as_user(
37
+ cmd: str,
38
+ /,
39
+ *args: str,
40
+ executable: str | None = None,
41
+ shell: bool = False,
42
+ cwd: PathLike | None = None,
43
+ env: StrStrMapping | None = None,
44
+ user: str | int | None = None,
45
+ print: bool = False, # noqa: A002
46
+ print_stdout: bool = False,
47
+ print_stderr: bool = False,
48
+ suppress: bool = False,
49
+ retry: Retry | None = None,
50
+ retry_skip: Callable[[int, str, str], bool] | None = None,
51
+ logger: LoggerLike | None = None,
52
+ ) -> None:
53
+ if user is None:
54
+ run(
55
+ cmd,
56
+ *args,
57
+ executable=executable,
58
+ shell=shell,
59
+ cwd=cwd,
60
+ env=env,
61
+ print=print,
62
+ print_stdout=print_stdout,
63
+ print_stderr=print_stderr,
64
+ suppress=suppress,
65
+ retry=retry,
66
+ retry_skip=retry_skip,
67
+ logger=logger,
68
+ )
69
+ else:
70
+ run(
71
+ "su",
72
+ "-",
73
+ str(user),
74
+ executable=executable,
75
+ shell=shell,
76
+ cwd=cwd,
77
+ env=env,
78
+ input=join([cmd, *args]),
79
+ print=print,
80
+ print_stdout=print_stdout,
81
+ print_stderr=print_stderr,
82
+ suppress=suppress,
83
+ retry=retry,
84
+ retry_skip=retry_skip,
85
+ logger=logger,
86
+ )
87
+
88
+
89
+ __all__ = ["to_repo_num"]
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from postgres.commands._backup import backup, make_backup_cmd
4
+ from postgres.commands._check import check, make_check_cmd
5
+ from postgres.commands._info import info, make_info_cmd
6
+ from postgres.commands._restore import make_restore_cmd, restore
7
+ from postgres.commands._stanza_create import make_stanza_create_cmd, stanza_create
8
+ from postgres.commands._start import make_start_cmd, start
9
+ from postgres.commands._stop import make_stop_cmd, stop
10
+
11
+ __all__ = [
12
+ "backup",
13
+ "check",
14
+ "info",
15
+ "make_backup_cmd",
16
+ "make_check_cmd",
17
+ "make_info_cmd",
18
+ "make_restore_cmd",
19
+ "make_stanza_create_cmd",
20
+ "make_start_cmd",
21
+ "make_stop_cmd",
22
+ "restore",
23
+ "stanza_create",
24
+ "start",
25
+ "stop",
26
+ ]
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from click import command
6
+ from utilities.click import CONTEXT_SETTINGS
7
+ from utilities.core import always_iterable, is_pytest, set_up_logging, to_logger
8
+
9
+ from postgres import __version__
10
+ from postgres._click import (
11
+ repo_option,
12
+ stanza_argument,
13
+ type_default_option,
14
+ user_option,
15
+ )
16
+ from postgres._enums import DEFAULT_TYPE
17
+ from postgres._utilities import run_or_as_user, to_repo_num
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Callable, Mapping
21
+
22
+ from click import Command
23
+ from utilities.types import MaybeIterable
24
+
25
+ from postgres._enums import Type
26
+ from postgres._types import Repo
27
+
28
+
29
+ _LOGGER = to_logger(__name__)
30
+
31
+
32
+ ##
33
+
34
+
35
+ def backup(
36
+ stanza: str,
37
+ /,
38
+ *,
39
+ repo: MaybeIterable[Repo] | None = None,
40
+ repo_mapping: Mapping[str, int] | None = None,
41
+ type_: Type = DEFAULT_TYPE,
42
+ user: str | None = None,
43
+ ) -> None:
44
+ if repo is None:
45
+ _backup_core(stanza, type_=type_, user=user)
46
+ else:
47
+ for repo_i in always_iterable(repo):
48
+ _backup_core(
49
+ stanza, repo=repo_i, repo_mapping=repo_mapping, type_=type_, user=user
50
+ )
51
+
52
+
53
+ def _backup_core(
54
+ stanza: str,
55
+ /,
56
+ *,
57
+ repo: Repo | None = None,
58
+ repo_mapping: Mapping[str, int] | None = None,
59
+ type_: Type = DEFAULT_TYPE,
60
+ user: str | None = None,
61
+ ) -> None:
62
+ args: list[str] = ["pgbackrest"]
63
+ if repo is None:
64
+ _LOGGER.info("%s backup %r to default repo...", type_.desc.title(), stanza)
65
+ repo_num = repo
66
+ else:
67
+ _LOGGER.info("%s backup %r to repo %r...", type_.desc.title(), stanza, repo)
68
+ repo_num = to_repo_num(repo=repo, mapping=repo_mapping)
69
+ args.append(f"--repo={repo_num}")
70
+ args.extend([f"--stanza={stanza}", f"--type={type_.value}", "backup"])
71
+ run_or_as_user(*args, user=user, print=True, logger=_LOGGER)
72
+ if repo is None:
73
+ _LOGGER.info("Finished %s backup %r to default repo", type_.desc, stanza)
74
+ else:
75
+ _LOGGER.info("Finished %s backup %r to repo %r", type_.desc, stanza, repo)
76
+
77
+
78
+ ##
79
+
80
+
81
+ def make_backup_cmd(
82
+ *, cli: Callable[..., Command] = command, name: str | None = None
83
+ ) -> Command:
84
+ @stanza_argument
85
+ @type_default_option
86
+ @repo_option
87
+ @user_option
88
+ def func(*, stanza: str, type_: Type, repo: Repo | None, user: str | None) -> None:
89
+ if is_pytest():
90
+ return
91
+ set_up_logging(__name__, root=True, log_version=__version__)
92
+ backup(stanza, repo=repo, type_=type_, user=user)
93
+
94
+ return cli(name=name, help="Backup a database cluster", **CONTEXT_SETTINGS)(func)
95
+
96
+
97
+ __all__ = ["backup", "make_backup_cmd"]
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from click import command
6
+ from utilities.click import CONTEXT_SETTINGS
7
+ from utilities.core import is_pytest, set_up_logging, to_logger
8
+
9
+ from postgres import __version__
10
+ from postgres._click import stanza_option, user_option
11
+ from postgres._utilities import run_or_as_user
12
+
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Callable
15
+
16
+ from click import Command
17
+
18
+
19
+ _LOGGER = to_logger(__name__)
20
+
21
+
22
+ ##
23
+
24
+
25
+ def check(*, stanza: str | None = None, user: str | None = None) -> None:
26
+ _LOGGER.info("Checking configuration...")
27
+ args: list[str] = ["pgbackrest"]
28
+ if stanza is not None:
29
+ args.append(f"--stanza={stanza}")
30
+ run_or_as_user(*args, user=user, print=True, logger=_LOGGER)
31
+ _LOGGER.info("Finished checking configuration")
32
+
33
+
34
+ ##
35
+
36
+
37
+ def make_check_cmd(
38
+ *, cli: Callable[..., Command] = command, name: str | None = None
39
+ ) -> Command:
40
+ @stanza_option
41
+ @user_option
42
+ def func(*, stanza: str | None, user: str | None) -> None:
43
+ if is_pytest():
44
+ return
45
+ set_up_logging(__name__, root=True, log_version=__version__)
46
+ check(stanza=stanza, user=user)
47
+
48
+ return cli(name=name, help="Check the configuration", **CONTEXT_SETTINGS)(func)
49
+
50
+
51
+ __all__ = ["check", "make_check_cmd"]
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from click import command
6
+ from utilities.click import CONTEXT_SETTINGS
7
+ from utilities.core import is_pytest, set_up_logging, to_logger
8
+
9
+ from postgres import __version__
10
+ from postgres._click import (
11
+ repo_option,
12
+ stanza_option,
13
+ type_no_default_option,
14
+ user_option,
15
+ )
16
+ from postgres._utilities import run_or_as_user, to_repo_num
17
+
18
+ if TYPE_CHECKING:
19
+ from collections.abc import Callable, Mapping
20
+
21
+ from click import Command
22
+
23
+ from postgres._enums import Type
24
+ from postgres._types import Repo
25
+
26
+
27
+ _LOGGER = to_logger(__name__)
28
+
29
+
30
+ ##
31
+
32
+
33
+ def info(
34
+ *,
35
+ repo: Repo | None = None,
36
+ repo_mapping: Mapping[str, int] | None = None,
37
+ stanza: str | None = None,
38
+ type_: Type | None = None,
39
+ user: str | None = None,
40
+ ) -> None:
41
+ _LOGGER.info("Getting info...")
42
+ args: list[str] = ["pgbackrest"]
43
+ if repo is not None:
44
+ repo_num = to_repo_num(repo=repo, mapping=repo_mapping)
45
+ args.append(f"--repo={repo_num}")
46
+ if stanza is not None:
47
+ args.append(f"--stanza={stanza}")
48
+ if type_ is not None:
49
+ args.append(f"--type={type_.value}")
50
+ run_or_as_user(*args, user=user, print=True, logger=_LOGGER)
51
+ _LOGGER.info("Finished getting info")
52
+
53
+
54
+ ##
55
+
56
+
57
+ def make_info_cmd(
58
+ *, cli: Callable[..., Command] = command, name: str | None = None
59
+ ) -> Command:
60
+ @repo_option
61
+ @stanza_option
62
+ @type_no_default_option
63
+ @user_option
64
+ def func(
65
+ *, repo: Repo | None, stanza: str | None, type_: Type | None, user: str | None
66
+ ) -> None:
67
+ if is_pytest():
68
+ return
69
+ set_up_logging(__name__, root=True, log_version=__version__)
70
+ info(repo=repo, stanza=stanza, type_=type_, user=user)
71
+
72
+ return cli(
73
+ name=name, help="Retrieve information about backups", **CONTEXT_SETTINGS
74
+ )(func)
75
+
76
+
77
+ __all__ = ["info", "make_info_cmd"]
@@ -0,0 +1,142 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ from click import Command, command
8
+ from utilities.click import CONTEXT_SETTINGS, Str, argument, option
9
+ from utilities.core import is_pytest, set_up_logging, to_logger
10
+ from utilities.subprocess import run
11
+ from utilities.types import PathLike
12
+
13
+ from postgres import __version__
14
+ from postgres._click import repo_option, stanza_argument, user_option, version_option
15
+ from postgres._constants import POSTGRES_VERSION
16
+ from postgres._types import Repo
17
+ from postgres._utilities import run_or_as_user, to_repo_num
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Callable, Mapping
21
+
22
+ from utilities.types import PathLike
23
+
24
+ from postgres._types import Repo
25
+
26
+
27
+ _LOGGER = to_logger(__name__)
28
+
29
+
30
+ ##
31
+
32
+
33
+ def restore(
34
+ cluster: str,
35
+ stanza: str,
36
+ /,
37
+ *,
38
+ version: int = POSTGRES_VERSION,
39
+ repo: Repo | None = None,
40
+ repo_mapping: Mapping[str, int] | None = None,
41
+ target_timeline: int | None = None,
42
+ user: str | None = None,
43
+ ) -> None:
44
+ _LOGGER.info("Restoring Postgres...")
45
+ _stop_cluster(cluster, version=version)
46
+ _delete_data(cluster, version=version)
47
+ _run_restore(
48
+ stanza,
49
+ repo=repo,
50
+ repo_mapping=repo_mapping,
51
+ target_timeline=target_timeline,
52
+ user=user,
53
+ )
54
+ _start_cluster(cluster, version=version)
55
+ _LOGGER.info("Finished restoring Postgres")
56
+
57
+
58
+ def _stop_cluster(cluster: str, /, *, version: int = POSTGRES_VERSION) -> None:
59
+ _LOGGER.info("Stopping cluster %r...", cluster)
60
+ run("pg_ctlcluster", str(version), cluster, "stop", suppress=True)
61
+
62
+
63
+ def _delete_data(
64
+ name: str, /, *, version: int = POSTGRES_VERSION, __root: PathLike | None = None
65
+ ) -> None:
66
+ _LOGGER.info("Deleting %r data files...", name)
67
+ root = Path("/") if __root is None else Path(__root)
68
+ path = root / f"var/lib/postgresql/{version}/{name}"
69
+ run("find", str(path), "-mindepth", "1", "-delete")
70
+
71
+
72
+ def _run_restore(
73
+ stanza: str,
74
+ /,
75
+ *,
76
+ repo: Repo | None = None,
77
+ repo_mapping: Mapping[str, int] | None = None,
78
+ target_timeline: int | None = None,
79
+ user: str | None = None,
80
+ ) -> None:
81
+ args: list[str] = ["pgbackrest"]
82
+ if repo is None:
83
+ _LOGGER.info("Restoring default repo to %r...", stanza)
84
+ else:
85
+ _LOGGER.info("Restoring repo %r to %r...", repo, stanza)
86
+ repo_num = to_repo_num(repo=repo, mapping=repo_mapping)
87
+ args.append(f"--repo={repo_num}")
88
+ args.append(f"--stanza={stanza}")
89
+ if target_timeline is not None:
90
+ args.append(f"--target_timeline={target_timeline}")
91
+ args.append("restore")
92
+ run_or_as_user(*args, user=user, print=True, logger=_LOGGER)
93
+ if repo is None:
94
+ _LOGGER.info("Finished restoring default repo to %r...", stanza)
95
+ else:
96
+ _LOGGER.info("Finished restoring repo %r to %r", repo, stanza)
97
+
98
+
99
+ def _start_cluster(name: str, /, *, version: int = POSTGRES_VERSION) -> None:
100
+ _LOGGER.info("Starting cluster %r...", name)
101
+ run("pg_ctlcluster", str(version), name, "start")
102
+
103
+
104
+ ##
105
+
106
+
107
+ def make_restore_cmd(
108
+ *, cli: Callable[..., Command] = command, name: str | None = None
109
+ ) -> Command:
110
+ @argument("cluster", type=Str())
111
+ @stanza_argument
112
+ @version_option
113
+ @repo_option
114
+ @option(
115
+ "--target-timeline", type=int, default=None, help="Recover along a timeline"
116
+ )
117
+ @user_option
118
+ def func(
119
+ *,
120
+ cluster: str,
121
+ stanza: str,
122
+ version: int,
123
+ repo: Repo | None,
124
+ target_timeline: int | None,
125
+ user: str | None,
126
+ ) -> None:
127
+ if is_pytest():
128
+ return
129
+ set_up_logging(__name__, root=True, log_version=__version__)
130
+ restore(
131
+ cluster,
132
+ stanza,
133
+ version=version,
134
+ repo=repo,
135
+ target_timeline=target_timeline,
136
+ user=user,
137
+ )
138
+
139
+ return cli(name=name, help="Restore a database cluster", **CONTEXT_SETTINGS)(func)
140
+
141
+
142
+ __all__ = ["make_restore_cmd", "restore"]
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from click import command
6
+ from utilities.click import CONTEXT_SETTINGS
7
+ from utilities.core import is_pytest, set_up_logging, to_logger
8
+
9
+ from postgres import __version__
10
+ from postgres._click import stanza_argument, user_option
11
+ from postgres._utilities import run_or_as_user
12
+
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Callable
15
+
16
+ from click import Command
17
+
18
+
19
+ _LOGGER = to_logger(__name__)
20
+
21
+
22
+ ##
23
+
24
+
25
+ def stanza_create(stanza: str, /, *, user: str | None = None) -> None:
26
+ _LOGGER.info("Creating stanza...")
27
+ run_or_as_user(
28
+ "pgbackrest",
29
+ f"--stanza={stanza}",
30
+ "stanza-create",
31
+ user=user,
32
+ print=True,
33
+ logger=_LOGGER,
34
+ )
35
+ _LOGGER.info("Finished creating stanza")
36
+
37
+
38
+ ##
39
+
40
+
41
+ def make_stanza_create_cmd(
42
+ *, cli: Callable[..., Command] = command, name: str | None = None
43
+ ) -> Command:
44
+ @stanza_argument
45
+ @user_option
46
+ def func(*, stanza: str, user: str | None) -> None:
47
+ if is_pytest():
48
+ return
49
+ set_up_logging(__name__, root=True, log_version=__version__)
50
+ stanza_create(stanza, user=user)
51
+
52
+ return cli(name=name, help="Create the required stanza data", **CONTEXT_SETTINGS)(
53
+ func
54
+ )
55
+
56
+
57
+ __all__ = ["make_stanza_create_cmd", "stanza_create"]
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from click import command
6
+ from utilities.click import CONTEXT_SETTINGS
7
+ from utilities.core import is_pytest, set_up_logging, to_logger
8
+
9
+ from postgres import __version__
10
+ from postgres._click import stanza_option, user_option
11
+ from postgres._utilities import run_or_as_user
12
+
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Callable
15
+
16
+ from click import Command
17
+
18
+
19
+ _LOGGER = to_logger(__name__)
20
+
21
+
22
+ ##
23
+
24
+
25
+ def start(*, stanza: str | None = None, user: str | None = None) -> None:
26
+ _LOGGER.info("Starting 'pgbackrest'...")
27
+ args: list[str] = ["pgbackrest"]
28
+ if stanza is None:
29
+ args.append(f"--stanza={stanza}")
30
+ args.append("start")
31
+ run_or_as_user(*args, user=user, print=True, logger=_LOGGER)
32
+ _LOGGER.info("Finished starting 'pgbackrest'")
33
+
34
+
35
+ ##
36
+
37
+
38
+ def make_start_cmd(
39
+ *, cli: Callable[..., Command] = command, name: str | None = None
40
+ ) -> Command:
41
+ @stanza_option
42
+ @user_option
43
+ def func(*, stanza: str | None, user: str | None) -> None:
44
+ if is_pytest():
45
+ return
46
+ set_up_logging(__name__, root=True, log_version=__version__)
47
+ start(stanza=stanza, user=user)
48
+
49
+ return cli(name=name, help="Allow pgBackRest processes to run", **CONTEXT_SETTINGS)(
50
+ func
51
+ )
52
+
53
+
54
+ __all__ = ["make_start_cmd", "start"]
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from click import command
6
+ from utilities.click import CONTEXT_SETTINGS
7
+ from utilities.core import is_pytest, set_up_logging, to_logger
8
+
9
+ from postgres import __version__
10
+ from postgres._click import stanza_option, user_option
11
+ from postgres._utilities import run_or_as_user
12
+
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Callable
15
+
16
+ from click import Command
17
+
18
+
19
+ _LOGGER = to_logger(__name__)
20
+
21
+
22
+ ##
23
+
24
+
25
+ def stop(*, stanza: str | None = None, user: str | None = None) -> None:
26
+ _LOGGER.info("Stopping 'pgbackrest'...")
27
+ args: list[str] = ["pgbackrest"]
28
+ if stanza is None:
29
+ args.append(f"--stanza={stanza}")
30
+ args.append("stop")
31
+ run_or_as_user(*args, user=user, print=True, logger=_LOGGER)
32
+ _LOGGER.info("Finished stopping 'pgbackrest'")
33
+
34
+
35
+ ##
36
+
37
+
38
+ def make_stop_cmd(
39
+ *, cli: Callable[..., Command] = command, name: str | None = None
40
+ ) -> Command:
41
+ @stanza_option
42
+ @user_option
43
+ def func(*, stanza: str | None, user: str | None) -> None:
44
+ if is_pytest():
45
+ return
46
+ set_up_logging(__name__, root=True, log_version=__version__)
47
+ stop(stanza=stanza, user=user)
48
+
49
+ return cli(
50
+ name=name, help="Stop pgBackRest processes from running", **CONTEXT_SETTINGS
51
+ )(func)
52
+
53
+
54
+ __all__ = ["make_stop_cmd", "stop"]
postgres/py.typed ADDED
File without changes