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.
- dycw_postgres-0.2.0.dist-info/METADATA +17 -0
- dycw_postgres-0.2.0.dist-info/RECORD +20 -0
- dycw_postgres-0.2.0.dist-info/WHEEL +4 -0
- dycw_postgres-0.2.0.dist-info/entry_points.txt +10 -0
- postgres/__init__.py +32 -0
- postgres/_cli.py +47 -0
- postgres/_click.py +56 -0
- postgres/_constants.py +6 -0
- postgres/_enums.py +29 -0
- postgres/_types.py +6 -0
- postgres/_utilities.py +89 -0
- postgres/commands/__init__.py +26 -0
- postgres/commands/_backup.py +97 -0
- postgres/commands/_check.py +51 -0
- postgres/commands/_info.py +77 -0
- postgres/commands/_restore.py +142 -0
- postgres/commands/_stanza_create.py +57 -0
- postgres/commands/_start.py +54 -0
- postgres/commands/_stop.py +54 -0
- postgres/py.typed +0 -0
|
@@ -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,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
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
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
|