postgresql-charms-single-kernel 16.0.2__py3-none-any.whl → 16.1.1__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.
- {postgresql_charms_single_kernel-16.0.2.dist-info → postgresql_charms_single_kernel-16.1.1.dist-info}/METADATA +1 -1
- postgresql_charms_single_kernel-16.1.1.dist-info/RECORD +11 -0
- single_kernel_postgresql/abstract_charm.py +2 -1
- single_kernel_postgresql/config/literals.py +9 -0
- single_kernel_postgresql/utils/postgresql.py +99 -11
- postgresql_charms_single_kernel-16.0.2.dist-info/RECORD +0 -11
- {postgresql_charms_single_kernel-16.0.2.dist-info → postgresql_charms_single_kernel-16.1.1.dist-info}/WHEEL +0 -0
- {postgresql_charms_single_kernel-16.0.2.dist-info → postgresql_charms_single_kernel-16.1.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: postgresql-charms-single-kernel
|
|
3
|
-
Version: 16.
|
|
3
|
+
Version: 16.1.1
|
|
4
4
|
Summary: Shared and reusable code for PostgreSQL-related charms
|
|
5
5
|
Author-email: Canonical Data Platform <data-platform@lists.launchpad.net>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
single_kernel_postgresql/__init__.py,sha256=F57JUcML43xgR0kpj9csryZkAoDSBPLLRjzSTHPaTd4,116
|
|
2
|
+
single_kernel_postgresql/abstract_charm.py,sha256=jWYV7nTZLiwxCZEedMZwyXzU3iNDRUcz5ZdI4LGUkUw,841
|
|
3
|
+
single_kernel_postgresql/config/__init__.py,sha256=k9Ud5ZZNd3l0nn8xi8AIlT45oZk8hJ542BjAFGDJzuc,140
|
|
4
|
+
single_kernel_postgresql/config/literals.py,sha256=88r33TBX7SXqgpV6EEXtZmSJrfOtsFiVJ_omop0RuBo,643
|
|
5
|
+
single_kernel_postgresql/utils/__init__.py,sha256=VwAEW3wYjs99q38bid47aS6Os4s3catK4H5HfdPkp94,121
|
|
6
|
+
single_kernel_postgresql/utils/filesystem.py,sha256=CJ2iXqFPKM0NfqqZSTDlENOrM0RW7rmTmcuzwV0bR9A,552
|
|
7
|
+
single_kernel_postgresql/utils/postgresql.py,sha256=IiEOFzrpiDXRWgwnvywiul9f8gU8hxCBaSqVFUyFgjU,79617
|
|
8
|
+
postgresql_charms_single_kernel-16.1.1.dist-info/METADATA,sha256=UD9GAm4GA4tQKAaWsDiR5uyN-k921jq2nt8Uwq6sqsE,484
|
|
9
|
+
postgresql_charms_single_kernel-16.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
postgresql_charms_single_kernel-16.1.1.dist-info/top_level.txt,sha256=fH85HKyfDV3--1JuYe-rWJmN5XTlXVXGOBO5iNA36Rk,25
|
|
11
|
+
postgresql_charms_single_kernel-16.1.1.dist-info/RECORD,,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
from ops.charm import CharmBase
|
|
6
6
|
|
|
7
|
-
from .config.literals import SYSTEM_USERS, USER
|
|
7
|
+
from .config.literals import SYSTEM_USERS, USER, Substrates
|
|
8
8
|
from .utils.postgresql import PostgreSQL
|
|
9
9
|
|
|
10
10
|
|
|
@@ -15,6 +15,7 @@ class AbstractPostgreSQLCharm(CharmBase):
|
|
|
15
15
|
super().__init__(*args)
|
|
16
16
|
|
|
17
17
|
self.postgresql = PostgreSQL(
|
|
18
|
+
substrate=Substrates.VM,
|
|
18
19
|
primary_host="localhost",
|
|
19
20
|
current_host="localhost",
|
|
20
21
|
user=USER,
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
This module should contain the literals used in the charms (paths, enums, etc).
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from enum import Enum
|
|
9
|
+
|
|
8
10
|
# Permissions.
|
|
9
11
|
POSTGRESQL_STORAGE_PERMISSIONS = 0o700
|
|
10
12
|
|
|
@@ -19,3 +21,10 @@ REWIND_USER = "rewind"
|
|
|
19
21
|
SNAP_USER = "_daemon_"
|
|
20
22
|
USER = "operator"
|
|
21
23
|
SYSTEM_USERS = [MONITORING_USER, REPLICATION_USER, REWIND_USER, USER]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Substrates(str, Enum):
|
|
27
|
+
"""Possible substrates."""
|
|
28
|
+
|
|
29
|
+
K8S = "k8s"
|
|
30
|
+
VM = "vm"
|
|
@@ -30,7 +30,13 @@ import psycopg2
|
|
|
30
30
|
from ops import ConfigData
|
|
31
31
|
from psycopg2.sql import SQL, Identifier, Literal
|
|
32
32
|
|
|
33
|
-
from ..config.literals import
|
|
33
|
+
from ..config.literals import (
|
|
34
|
+
BACKUP_USER,
|
|
35
|
+
POSTGRESQL_STORAGE_PERMISSIONS,
|
|
36
|
+
SNAP_USER,
|
|
37
|
+
SYSTEM_USERS,
|
|
38
|
+
Substrates,
|
|
39
|
+
)
|
|
34
40
|
from .filesystem import change_owner
|
|
35
41
|
|
|
36
42
|
# Groups to distinguish HBA access
|
|
@@ -60,6 +66,7 @@ ALLOWED_ROLES = {
|
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
INVALID_DATABASE_NAME_BLOCKING_MESSAGE = "invalid database name"
|
|
69
|
+
INVALID_DATABASE_NAMES = ["databases", "postgres", "template0", "template1"]
|
|
63
70
|
INVALID_EXTRA_USER_ROLE_BLOCKING_MESSAGE = "invalid role(s) for extra user roles"
|
|
64
71
|
|
|
65
72
|
REQUIRED_PLUGINS = {
|
|
@@ -107,6 +114,10 @@ class PostgreSQLCreateUserError(PostgreSQLBaseError):
|
|
|
107
114
|
self.message = message
|
|
108
115
|
|
|
109
116
|
|
|
117
|
+
class PostgreSQLUpdateUserError(PostgreSQLBaseError):
|
|
118
|
+
"""Exception raised when creating a user fails."""
|
|
119
|
+
|
|
120
|
+
|
|
110
121
|
class PostgreSQLUndefinedHostError(PostgreSQLBaseError):
|
|
111
122
|
"""Exception when host is not set."""
|
|
112
123
|
|
|
@@ -139,6 +150,10 @@ class PostgreSQLGetPostgreSQLVersionError(PostgreSQLBaseError):
|
|
|
139
150
|
"""Exception raised when retrieving PostgreSQL version fails."""
|
|
140
151
|
|
|
141
152
|
|
|
153
|
+
class PostgreSQLListDatabasesError(PostgreSQLBaseError):
|
|
154
|
+
"""Exception raised when retrieving the databases."""
|
|
155
|
+
|
|
156
|
+
|
|
142
157
|
class PostgreSQLListAccessibleDatabasesForUserError(PostgreSQLBaseError):
|
|
143
158
|
"""Exception raised when retrieving the accessible databases for a user fails."""
|
|
144
159
|
|
|
@@ -216,6 +231,7 @@ class PostgreSQL:
|
|
|
216
231
|
|
|
217
232
|
def __init__(
|
|
218
233
|
self,
|
|
234
|
+
substrate: Substrates,
|
|
219
235
|
primary_host: Optional[str],
|
|
220
236
|
current_host: Optional[str],
|
|
221
237
|
user: str,
|
|
@@ -223,6 +239,18 @@ class PostgreSQL:
|
|
|
223
239
|
database: str,
|
|
224
240
|
system_users: Optional[List[str]] = None,
|
|
225
241
|
):
|
|
242
|
+
"""Create a PostgreSQL helper.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
substrate: substrate where the charm is running (Substrates.K8S or Substrates.VM).
|
|
246
|
+
primary_host: hostname or address for primary database host.
|
|
247
|
+
current_host: hostname or address for the current database host.
|
|
248
|
+
user: username to connect as.
|
|
249
|
+
password: password for the user.
|
|
250
|
+
database: default database name.
|
|
251
|
+
system_users: list of system users.
|
|
252
|
+
"""
|
|
253
|
+
self.substrate = substrate
|
|
226
254
|
self.primary_host = primary_host
|
|
227
255
|
self.current_host = current_host
|
|
228
256
|
self.user = user
|
|
@@ -322,7 +350,7 @@ class PostgreSQL:
|
|
|
322
350
|
if len(database) > 49:
|
|
323
351
|
logger.error(f"Invalid database name (it must not exceed 49 characters): {database}.")
|
|
324
352
|
raise PostgreSQLCreateDatabaseError(INVALID_DATABASE_NAME_BLOCKING_MESSAGE)
|
|
325
|
-
if database in
|
|
353
|
+
if database in INVALID_DATABASE_NAMES:
|
|
326
354
|
logger.error(f"Invalid database name: {database}.")
|
|
327
355
|
raise PostgreSQLCreateDatabaseError(INVALID_DATABASE_NAME_BLOCKING_MESSAGE)
|
|
328
356
|
plugins = plugins if plugins else []
|
|
@@ -419,14 +447,28 @@ class PostgreSQL:
|
|
|
419
447
|
Returns:
|
|
420
448
|
A tuple containing the adjusted user definition and a list of additional statements.
|
|
421
449
|
"""
|
|
450
|
+
db_roles, connect_statements = self._adjust_user_roles(user, roles, database)
|
|
451
|
+
if db_roles:
|
|
452
|
+
str_roles = [f'"{role}"' for role in db_roles]
|
|
453
|
+
user_definition += f" IN ROLE {', '.join(str_roles)}"
|
|
454
|
+
return user_definition, connect_statements
|
|
455
|
+
|
|
456
|
+
def _adjust_user_roles(
|
|
457
|
+
self, user: str, roles: Optional[List[str]], database: Optional[str]
|
|
458
|
+
) -> Tuple[List[str], List[str]]:
|
|
459
|
+
"""Adjusts the user definition to include additional statements.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
A tuple containing the adjusted user definition and a list of additional statements.
|
|
463
|
+
"""
|
|
464
|
+
db_roles = []
|
|
422
465
|
connect_statements = []
|
|
423
466
|
if database:
|
|
424
467
|
if roles is not None and not any(
|
|
425
|
-
|
|
426
|
-
for role in roles
|
|
427
|
-
if role in [ROLE_STATS, ROLE_READ, ROLE_DML, ROLE_BACKUP, ROLE_DBA]
|
|
468
|
+
role in [ROLE_STATS, ROLE_READ, ROLE_DML, ROLE_BACKUP, ROLE_DBA] for role in roles
|
|
428
469
|
):
|
|
429
|
-
|
|
470
|
+
db_roles.append(f"charmed_{database}_admin")
|
|
471
|
+
db_roles.append(f"charmed_{database}_dml")
|
|
430
472
|
else:
|
|
431
473
|
connect_statements.append(
|
|
432
474
|
SQL("GRANT CONNECT ON DATABASE {} TO {};").format(
|
|
@@ -434,9 +476,7 @@ class PostgreSQL:
|
|
|
434
476
|
)
|
|
435
477
|
)
|
|
436
478
|
if roles is not None and any(
|
|
437
|
-
|
|
438
|
-
for role in roles
|
|
439
|
-
if role
|
|
479
|
+
role
|
|
440
480
|
in [
|
|
441
481
|
ROLE_STATS,
|
|
442
482
|
ROLE_READ,
|
|
@@ -446,6 +486,7 @@ class PostgreSQL:
|
|
|
446
486
|
ROLE_ADMIN,
|
|
447
487
|
ROLE_DATABASES_OWNER,
|
|
448
488
|
]
|
|
489
|
+
for role in roles
|
|
449
490
|
):
|
|
450
491
|
for system_database in ["postgres", "template1"]:
|
|
451
492
|
connect_statements.append(
|
|
@@ -453,7 +494,7 @@ class PostgreSQL:
|
|
|
453
494
|
Identifier(system_database), Identifier(user)
|
|
454
495
|
)
|
|
455
496
|
)
|
|
456
|
-
return
|
|
497
|
+
return db_roles, connect_statements
|
|
457
498
|
|
|
458
499
|
def _process_extra_user_roles(
|
|
459
500
|
self, user: str, extra_user_roles: Optional[List[str]] = None
|
|
@@ -1077,7 +1118,7 @@ class PostgreSQL:
|
|
|
1077
1118
|
if temp_location is not None:
|
|
1078
1119
|
# Fix permissions on the temporary tablespace location when a reboot happens and tmpfs is being used.
|
|
1079
1120
|
temp_location_stats = os.stat(temp_location)
|
|
1080
|
-
if (
|
|
1121
|
+
if self.substrate == Substrates.VM and (
|
|
1081
1122
|
pwd.getpwuid(temp_location_stats.st_uid).pw_name != SNAP_USER
|
|
1082
1123
|
or int(temp_location_stats.st_mode & 0o777) != POSTGRESQL_STORAGE_PERMISSIONS
|
|
1083
1124
|
):
|
|
@@ -1821,3 +1862,50 @@ $$ LANGUAGE plpgsql security definer;""" # noqa: S608
|
|
|
1821
1862
|
finally:
|
|
1822
1863
|
if connection:
|
|
1823
1864
|
connection.close()
|
|
1865
|
+
|
|
1866
|
+
def list_databases(self, prefix: Optional[str] = None) -> List[str]:
|
|
1867
|
+
"""List non-system databases starting with prefix."""
|
|
1868
|
+
prefix_stmt = (
|
|
1869
|
+
SQL(" AND datname LIKE {}").format(Literal(prefix + "%")) if prefix else SQL("")
|
|
1870
|
+
)
|
|
1871
|
+
try:
|
|
1872
|
+
with self._connect_to_database() as connection, connection.cursor() as cursor:
|
|
1873
|
+
cursor.execute(
|
|
1874
|
+
SQL(
|
|
1875
|
+
"SELECT datname FROM pg_database WHERE datistemplate = false AND datname <>'postgres'{};"
|
|
1876
|
+
).format(prefix_stmt)
|
|
1877
|
+
)
|
|
1878
|
+
return [row[0] for row in cursor.fetchall()]
|
|
1879
|
+
except psycopg2.Error as e:
|
|
1880
|
+
raise PostgreSQLListDatabasesError() from e
|
|
1881
|
+
finally:
|
|
1882
|
+
if connection:
|
|
1883
|
+
connection.close()
|
|
1884
|
+
|
|
1885
|
+
def add_user_to_databases(
|
|
1886
|
+
self, user: str, databases: List[str], extra_user_roles: Optional[List[str]] = None
|
|
1887
|
+
) -> None:
|
|
1888
|
+
"""Grant user access to database."""
|
|
1889
|
+
try:
|
|
1890
|
+
roles, _ = self._process_extra_user_roles(user, extra_user_roles)
|
|
1891
|
+
connect_stmt = []
|
|
1892
|
+
for database in databases:
|
|
1893
|
+
db_roles, db_connect_stmt = self._adjust_user_roles(user, roles, database)
|
|
1894
|
+
roles += db_roles
|
|
1895
|
+
connect_stmt += db_connect_stmt
|
|
1896
|
+
with self._connect_to_database() as connection, connection.cursor() as cursor:
|
|
1897
|
+
cursor.execute(SQL("RESET ROLE;"))
|
|
1898
|
+
cursor.execute(SQL("BEGIN;"))
|
|
1899
|
+
cursor.execute(SQL("SET LOCAL log_statement = 'none';"))
|
|
1900
|
+
cursor.execute(SQL("COMMIT;"))
|
|
1901
|
+
|
|
1902
|
+
# Add extra user roles to the new user.
|
|
1903
|
+
for role in roles:
|
|
1904
|
+
cursor.execute(
|
|
1905
|
+
SQL("GRANT {} TO {};").format(Identifier(role), Identifier(user))
|
|
1906
|
+
)
|
|
1907
|
+
for statement in connect_stmt:
|
|
1908
|
+
cursor.execute(statement)
|
|
1909
|
+
except psycopg2.Error as e:
|
|
1910
|
+
logger.error(f"Failed to create user: {e}")
|
|
1911
|
+
raise PostgreSQLUpdateUserError() from e
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
single_kernel_postgresql/__init__.py,sha256=F57JUcML43xgR0kpj9csryZkAoDSBPLLRjzSTHPaTd4,116
|
|
2
|
-
single_kernel_postgresql/abstract_charm.py,sha256=grlJCmpz8rq7_NKVkyuoK7_8I2r6DRkh5DhbtyPWna8,792
|
|
3
|
-
single_kernel_postgresql/config/__init__.py,sha256=k9Ud5ZZNd3l0nn8xi8AIlT45oZk8hJ542BjAFGDJzuc,140
|
|
4
|
-
single_kernel_postgresql/config/literals.py,sha256=4EpAnoxCEMfOCPpFD5dyAU_0h2OwAHgEX9OjTWNcaQg,527
|
|
5
|
-
single_kernel_postgresql/utils/__init__.py,sha256=VwAEW3wYjs99q38bid47aS6Os4s3catK4H5HfdPkp94,121
|
|
6
|
-
single_kernel_postgresql/utils/filesystem.py,sha256=CJ2iXqFPKM0NfqqZSTDlENOrM0RW7rmTmcuzwV0bR9A,552
|
|
7
|
-
single_kernel_postgresql/utils/postgresql.py,sha256=ZhTZZcshgHjZgP4aRCWyfmW9ArUISLdqo9NnJBg1fdM,75963
|
|
8
|
-
postgresql_charms_single_kernel-16.0.2.dist-info/METADATA,sha256=TCKebjoRLWERCEzeLgiRcX9dfZx-TLXf0CU55Ki7xik,484
|
|
9
|
-
postgresql_charms_single_kernel-16.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
-
postgresql_charms_single_kernel-16.0.2.dist-info/top_level.txt,sha256=fH85HKyfDV3--1JuYe-rWJmN5XTlXVXGOBO5iNA36Rk,25
|
|
11
|
-
postgresql_charms_single_kernel-16.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|