pglift-cli 1.7.0__tar.gz → 1.8.0__tar.gz
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.
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/PKG-INFO +6 -6
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/pyproject.toml +3 -3
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/base.py +1 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/instance.py +17 -6
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/pgconf.py +6 -6
- pglift_cli-1.8.0/src/pglift_cli/pghba.py +57 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/role.py +12 -3
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/util.py +15 -1
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/.gitignore +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/README.md +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/hatch.toml +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/__init__.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/__main__.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/_settings.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/_site.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/console.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/database.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/hookspecs.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/main.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/model.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/patroni.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/pgbackrest/__init__.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/pgbackrest/repo_path.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/pm.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/postgres.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/prometheus.py +0 -0
- {pglift_cli-1.7.0 → pglift_cli-1.8.0}/src/pglift_cli/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pglift_cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: Command-line interface for pglift
|
|
5
5
|
Project-URL: Documentation, https://pglift.readthedocs.io/
|
|
6
6
|
Project-URL: Source, https://gitlab.com/dalibo/pglift/
|
|
@@ -28,16 +28,16 @@ Requires-Dist: pluggy
|
|
|
28
28
|
Requires-Dist: psycopg>=3.1
|
|
29
29
|
Requires-Dist: pydantic>=2.5.0
|
|
30
30
|
Requires-Dist: pyyaml>=6.0.1
|
|
31
|
-
Requires-Dist: rich
|
|
31
|
+
Requires-Dist: rich!=13.9.0,>=11.0.0
|
|
32
32
|
Provides-Extra: dev
|
|
33
33
|
Requires-Dist: anyio; extra == 'dev'
|
|
34
|
-
Requires-Dist: mypy!=1.11.*,>=1.10.0; (python_version < '3.10') and extra == 'dev'
|
|
34
|
+
Requires-Dist: mypy!=1.11.*,!=1.12.*,>=1.10.0; (python_version < '3.10') and extra == 'dev'
|
|
35
35
|
Requires-Dist: mypy>=1.10.0; (python_version >= '3.10') and extra == 'dev'
|
|
36
36
|
Requires-Dist: patroni[etcd]>=2.1.5; extra == 'dev'
|
|
37
37
|
Requires-Dist: port-for; extra == 'dev'
|
|
38
38
|
Requires-Dist: prysk[pytest-plugin]>=0.14.0; extra == 'dev'
|
|
39
|
-
Requires-Dist: pytest; extra == 'dev'
|
|
40
39
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
41
41
|
Requires-Dist: trustme; extra == 'dev'
|
|
42
42
|
Requires-Dist: types-pyyaml>=6.0.12.10; extra == 'dev'
|
|
43
43
|
Provides-Extra: test
|
|
@@ -45,11 +45,11 @@ Requires-Dist: anyio; extra == 'test'
|
|
|
45
45
|
Requires-Dist: patroni[etcd]>=2.1.5; extra == 'test'
|
|
46
46
|
Requires-Dist: port-for; extra == 'test'
|
|
47
47
|
Requires-Dist: prysk[pytest-plugin]>=0.14.0; extra == 'test'
|
|
48
|
-
Requires-Dist: pytest; extra == 'test'
|
|
49
48
|
Requires-Dist: pytest-cov; extra == 'test'
|
|
49
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
50
50
|
Requires-Dist: trustme; extra == 'test'
|
|
51
51
|
Provides-Extra: typing
|
|
52
|
-
Requires-Dist: mypy!=1.11.*,>=1.10.0; (python_version < '3.10') and extra == 'typing'
|
|
52
|
+
Requires-Dist: mypy!=1.11.*,!=1.12.*,>=1.10.0; (python_version < '3.10') and extra == 'typing'
|
|
53
53
|
Requires-Dist: mypy>=1.10.0; (python_version >= '3.10') and extra == 'typing'
|
|
54
54
|
Requires-Dist: types-pyyaml>=6.0.12.10; extra == 'typing'
|
|
55
55
|
Description-Content-Type: text/markdown
|
|
@@ -43,7 +43,7 @@ dependencies = [
|
|
|
43
43
|
"pluggy",
|
|
44
44
|
"psycopg >= 3.1",
|
|
45
45
|
"pydantic >= 2.5.0",
|
|
46
|
-
"rich >= 11.0.0",
|
|
46
|
+
"rich >= 11.0.0, != 13.9.0",
|
|
47
47
|
]
|
|
48
48
|
|
|
49
49
|
[project.optional-dependencies]
|
|
@@ -52,13 +52,13 @@ test = [
|
|
|
52
52
|
"patroni[etcd] >= 2.1.5",
|
|
53
53
|
"port-for",
|
|
54
54
|
"prysk[pytest-plugin] >= 0.14.0",
|
|
55
|
-
"pytest",
|
|
55
|
+
"pytest >= 8",
|
|
56
56
|
"pytest-cov",
|
|
57
57
|
"trustme",
|
|
58
58
|
]
|
|
59
59
|
typing = [
|
|
60
60
|
"mypy >= 1.10.0 ; python_version >= '3.10'",
|
|
61
|
-
"mypy >= 1.10.0, != 1.11.* ; python_version < '3.10'",
|
|
61
|
+
"mypy >= 1.10.0, != 1.11.*, != 1.12.* ; python_version < '3.10'",
|
|
62
62
|
"types-PyYAML >= 6.0.12.10",
|
|
63
63
|
]
|
|
64
64
|
dev = [
|
|
@@ -14,6 +14,7 @@ from pydantic.v1.utils import deep_update
|
|
|
14
14
|
|
|
15
15
|
from pglift import (
|
|
16
16
|
async_hooks,
|
|
17
|
+
exceptions,
|
|
17
18
|
hooks,
|
|
18
19
|
hookspecs,
|
|
19
20
|
instances,
|
|
@@ -127,7 +128,7 @@ async def alter(obj: Obj, instance: system.Instance, **changes: Any) -> None:
|
|
|
127
128
|
values = deep_update(values, changes)
|
|
128
129
|
# No need for 'settings' in validation_context() as a 'version' key
|
|
129
130
|
# must be present in 'values' when altering.
|
|
130
|
-
with validation_context(operation="update"):
|
|
131
|
+
with validation_context(operation="update", instance=manifest):
|
|
131
132
|
altered = _site.INSTANCE_MODEL.model_validate(values)
|
|
132
133
|
await instances.apply(
|
|
133
134
|
_site.SETTINGS, altered, _is_running=status == Status.running
|
|
@@ -149,10 +150,18 @@ async def apply(
|
|
|
149
150
|
version = default_postgresql_version(_site.SETTINGS.postgresql)
|
|
150
151
|
elif not isinstance(version, str):
|
|
151
152
|
version = str(version)
|
|
152
|
-
op: Operation =
|
|
153
|
-
|
|
154
|
-
)
|
|
155
|
-
|
|
153
|
+
op: Operation = "create"
|
|
154
|
+
actual: interface.Instance | None = None
|
|
155
|
+
if data.get("state") == "absent":
|
|
156
|
+
op = "update"
|
|
157
|
+
else:
|
|
158
|
+
try:
|
|
159
|
+
actual = await instances.get((name, version), settings=_site.SETTINGS)
|
|
160
|
+
except exceptions.InstanceNotFound:
|
|
161
|
+
pass
|
|
162
|
+
else:
|
|
163
|
+
op = "update"
|
|
164
|
+
with validation_context(operation=op, settings=_site.SETTINGS, instance=actual):
|
|
156
165
|
instance = _site.INSTANCE_MODEL.model_validate(data)
|
|
157
166
|
if dry_run:
|
|
158
167
|
ret = interface.InstanceApplyResult(change_state=None)
|
|
@@ -196,7 +205,9 @@ async def get(instance: system.Instance, output_format: OutputFormat | None) ->
|
|
|
196
205
|
}
|
|
197
206
|
if not instance.postgresql.standby:
|
|
198
207
|
exclude.add("standby")
|
|
199
|
-
print_table_for(
|
|
208
|
+
print_table_for(
|
|
209
|
+
[i], partial(model_dump, exclude=exclude, mode="pretty"), box=None
|
|
210
|
+
)
|
|
200
211
|
|
|
201
212
|
|
|
202
213
|
@cli.command("list")
|
|
@@ -105,10 +105,10 @@ async def set_(obj: Obj, instance: system.Instance, parameters: dict[str, Any])
|
|
|
105
105
|
status = await postgresql.status(pg_instance)
|
|
106
106
|
manifest = await instances._get(instance, status)
|
|
107
107
|
manifest.settings.update(parameters)
|
|
108
|
-
|
|
108
|
+
r = await instances.configure(
|
|
109
109
|
pg_instance, manifest, _is_running=status == Status.running
|
|
110
110
|
)
|
|
111
|
-
show_configuration_changes(changes, parameters.keys())
|
|
111
|
+
show_configuration_changes(r.changes, parameters.keys())
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
@cli.command("remove")
|
|
@@ -129,10 +129,10 @@ async def remove(obj: Obj, instance: system.Instance, parameters: tuple[str]) ->
|
|
|
129
129
|
raise click.ClickException(
|
|
130
130
|
f"{p!r} not found in managed configuration"
|
|
131
131
|
) from None
|
|
132
|
-
|
|
132
|
+
r = await instances.configure(
|
|
133
133
|
pg_instance, manifest, _is_running=status == Status.running
|
|
134
134
|
)
|
|
135
|
-
show_configuration_changes(changes, parameters)
|
|
135
|
+
show_configuration_changes(r.changes, parameters)
|
|
136
136
|
|
|
137
137
|
|
|
138
138
|
@cli.command("edit")
|
|
@@ -156,7 +156,7 @@ async def edit(obj: Obj, instance: system.Instance) -> None:
|
|
|
156
156
|
manifest = await instances._get(instance, status)
|
|
157
157
|
manifest.settings.clear()
|
|
158
158
|
manifest.settings.update(values)
|
|
159
|
-
|
|
159
|
+
r = await instances.configure(
|
|
160
160
|
pg_instance, manifest, _is_running=status == Status.running
|
|
161
161
|
)
|
|
162
|
-
show_configuration_changes(changes)
|
|
162
|
+
show_configuration_changes(r.changes)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 Dalibo
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from pglift import hba
|
|
12
|
+
from pglift.models import interface, system
|
|
13
|
+
|
|
14
|
+
from . import model
|
|
15
|
+
from .util import (
|
|
16
|
+
Group,
|
|
17
|
+
Obj,
|
|
18
|
+
audit,
|
|
19
|
+
instance_identifier_option,
|
|
20
|
+
pass_postgresql_instance,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.group(cls=Group)
|
|
25
|
+
@instance_identifier_option
|
|
26
|
+
def cli(**kwargs: Any) -> None:
|
|
27
|
+
"""Manage entries in the pg_hba.conf file of a PostgreSQL instance."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@cli.command("add")
|
|
31
|
+
@model.as_parameters(interface.HbaRecord, "create")
|
|
32
|
+
@pass_postgresql_instance
|
|
33
|
+
@click.pass_obj
|
|
34
|
+
def add(
|
|
35
|
+
obj: Obj, instance: system.PostgreSQLInstance, hbarecord: interface.HbaRecord
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Add a record in pg_hba.conf.
|
|
38
|
+
|
|
39
|
+
If no --connection-* option is specified, a 'local' record is added.
|
|
40
|
+
"""
|
|
41
|
+
with obj.lock, audit():
|
|
42
|
+
hba.add(instance, hbarecord)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@cli.command("remove")
|
|
46
|
+
@model.as_parameters(interface.HbaRecord, "create")
|
|
47
|
+
@pass_postgresql_instance
|
|
48
|
+
@click.pass_obj
|
|
49
|
+
def remove(
|
|
50
|
+
obj: Obj, instance: system.PostgreSQLInstance, hbarecord: interface.HbaRecord
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Remove a record from pg_hba.conf.
|
|
53
|
+
|
|
54
|
+
If no --connection-* option is specified, a 'local' record is removed.
|
|
55
|
+
"""
|
|
56
|
+
with obj.lock, audit():
|
|
57
|
+
hba.remove(instance, hbarecord)
|
|
@@ -125,7 +125,12 @@ async def apply(
|
|
|
125
125
|
) -> None:
|
|
126
126
|
"""Apply manifest as a role"""
|
|
127
127
|
op: Operation = (
|
|
128
|
-
"update"
|
|
128
|
+
"update"
|
|
129
|
+
if (
|
|
130
|
+
data.get("state") == "absent"
|
|
131
|
+
or await roles.exists(instance, name=data["name"])
|
|
132
|
+
)
|
|
133
|
+
else "create"
|
|
129
134
|
)
|
|
130
135
|
with validation_context(operation=op, settings=_site.SETTINGS):
|
|
131
136
|
role = _site.ROLE_MODEL.model_validate(data)
|
|
@@ -152,7 +157,9 @@ async def ls(
|
|
|
152
157
|
if output_format == "json":
|
|
153
158
|
print_json_for([model_dump(r) for r in rls])
|
|
154
159
|
else:
|
|
155
|
-
print_table_for(
|
|
160
|
+
print_table_for(
|
|
161
|
+
rls, partial(model_dump, mode="pretty", exclude={"in_roles", "hba_records"})
|
|
162
|
+
)
|
|
156
163
|
|
|
157
164
|
|
|
158
165
|
@cli.command("get")
|
|
@@ -170,7 +177,9 @@ async def get(
|
|
|
170
177
|
print_json_for(model_dump(r))
|
|
171
178
|
else:
|
|
172
179
|
print_table_for(
|
|
173
|
-
[r],
|
|
180
|
+
[r],
|
|
181
|
+
partial(model_dump, mode="pretty", exclude={"in_roles", "hba_records"}),
|
|
182
|
+
box=None,
|
|
174
183
|
)
|
|
175
184
|
|
|
176
185
|
|
|
@@ -442,10 +442,19 @@ class ManifestData(TypedDict, total=False):
|
|
|
442
442
|
OutputFormat = Literal["json"]
|
|
443
443
|
|
|
444
444
|
|
|
445
|
+
def set_output_format(
|
|
446
|
+
ctx: click.Context, param: click.Parameter, value: OutputFormat
|
|
447
|
+
) -> OutputFormat:
|
|
448
|
+
assert ctx.obj.output_format is None
|
|
449
|
+
ctx.obj.output_format = value
|
|
450
|
+
return value
|
|
451
|
+
|
|
452
|
+
|
|
445
453
|
output_format_option = click.option(
|
|
446
454
|
"-o",
|
|
447
455
|
"--output-format",
|
|
448
456
|
type=click.Choice(typing.get_args(OutputFormat), case_sensitive=False),
|
|
457
|
+
callback=set_output_format,
|
|
449
458
|
help="Specify the output format.",
|
|
450
459
|
)
|
|
451
460
|
|
|
@@ -614,8 +623,11 @@ class Obj:
|
|
|
614
623
|
# instance_identifier_option decorator's callback.
|
|
615
624
|
_instance: str | system.PostgreSQLInstance
|
|
616
625
|
|
|
617
|
-
def __init__(
|
|
626
|
+
def __init__(
|
|
627
|
+
self, *, debug: bool = False, output_format: OutputFormat | None = None
|
|
628
|
+
) -> None:
|
|
618
629
|
self.debug = debug
|
|
630
|
+
self.output_format = output_format
|
|
619
631
|
|
|
620
632
|
@cached_property
|
|
621
633
|
def lock(self) -> filelock.FileLock:
|
|
@@ -660,6 +672,8 @@ class Command(click.Command):
|
|
|
660
672
|
raise click.Abort from None
|
|
661
673
|
except pydantic.ValidationError as e:
|
|
662
674
|
logger.debug("a validation error occurred", exc_info=obj.debug)
|
|
675
|
+
if context.obj.output_format == "json":
|
|
676
|
+
console.print_json(e.json(include_url=False, include_context=False))
|
|
663
677
|
raise click.ClickException(str(e)) from None
|
|
664
678
|
except exceptions.Error as e:
|
|
665
679
|
logger.debug("an internal error occurred", exc_info=obj.debug)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|