Skip to content

Conversation

marceloneppel
Copy link
Member

@marceloneppel marceloneppel commented May 29, 2025

Issue

Catalog/database level roles ({database-name}_admin and {database-name}_owner) are missing to complete what was specified at DA169 - Predefined roles in Postgres.

Solution

Create catalog/database level roles described at DA169 - Predefined roles in Postgres.

Create the charmed_databases_owner role, which allows the relation users requesting it to be able to create new databases.

Enable the login hook (to auto escalate relation users to the {database-name}_owner user when they log in).

TODO: lib/charms/postgresql_k8s/v0/postgresql.py will be moved to lib/charms/postgresql_k8s/v1/postgresql.py in a next PR.

Checklist

  • I have added or updated any relevant documentation.
  • I have cleaned any remaining cloud resources from my accounts.

Copy link

codecov bot commented May 29, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 70.23%. Comparing base (59e6ff2) to head (32663cb).
Report is 1 commits behind head on 16/edge.

Additional details and impacted files
@@           Coverage Diff            @@
##           16/edge     #921   +/-   ##
========================================
  Coverage    70.23%   70.23%           
========================================
  Files           16       16           
  Lines         3723     3723           
  Branches       541      541           
========================================
  Hits          2615     2615           
  Misses         974      974           
  Partials       134      134           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

marceloneppel and others added 16 commits May 29, 2025 15:26
Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Signed-off-by: Marcelo Henrique Neppel <[email protected]>
…oles' into feature/16_predefined_catalog_roles

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Signed-off-by: Marcelo Henrique Neppel <[email protected]>
shayancanonical and others added 5 commits June 5, 2025 06:22
…oles' into feature/16_predefined_dba_role

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
…into feature/16_predefined_roles_cleanup

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
…nup' into feature/16_predefined_catalog_roles

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
@marceloneppel marceloneppel changed the title Create set_up_predefined_catalog_roles_function [DPE-7500] Create catalog/database level roles Jun 5, 2025
…into feature/16_predefined_roles_cleanup

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
…nup' into feature/16_predefined_catalog_roles

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
@marceloneppel marceloneppel marked this pull request as ready for review June 5, 2025 15:48
…into feature/16_predefined_roles_cleanup

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
…nup' into feature/16_predefined_catalog_roles

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
connection.autocommit = True

with connection.cursor() as cursor:
cursor.execute("RESET ROLE;")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed because the relation user is now auto escalated to the {database-name}_owner user, which is not part of the charmed_read role (only the relation user is).



@pytest.mark.abort_on_fail
async def test_deploy(ops_test: OpsTest, charm) -> None:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The big logic from this function is mostly to allow retesting in the same model (by resetting to the roles from the beginning of the first test).

Comment on lines +979 to +984
def relations(ops_test: OpsTest, provider_app: str, requirer_app: str) -> list:
return [
relation
for relation in ops_test.model.applications[provider_app].relations
if not relation.is_peer and relation.requires.application_name == requirer_app
]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Way to check if the relation still exists (and avoid situations where we removed it and it's still alive when trying to re-relate).


try:
self.postgresql.create_predefined_roles()
self.postgresql.create_predefined_instance_roles()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just renaming to be clear on which types of predefined roles we are creating at this moment (i.e. the ones introduced by #881).

Comment on lines +79 to +81
logger.info(f"Resetting the user to the {username} user in the {instance}")
cursor.execute("RESET ROLE;")
check_connected_user(cursor, username, username, primary=read_write_endpoint)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed because the relation user is now auto escalated to the {database-name}_owner user, which is not part of the charmed_dba role (only the relation user is).

Comment on lines +115 to 120
self.charm.postgresql.create_database(database, plugins=plugins)

password = new_password()
self.charm.postgresql.create_user(
user, password, extra_user_roles=extra_user_roles, in_role=f"{database}_admin"
)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order of the calls was changed here because the catalog/database level roles are created along with the database, and we need to pass the {database-name}_admin to the user creation method.

if connection is not None:
connection.close()

def _generate_database_privileges_statements(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, the {database-name}_owner owns all the objects in the database.

Comment on lines 371 to 376
f"CREATE ROLE {ROLE_DBA} NOSUPERUSER CREATEDB NOCREATEROLE NOLOGIN NOREPLICATION;",
f"GRANT execute ON FUNCTION set_user(text) TO {ROLE_DBA};",
f"GRANT execute ON FUNCTION set_user(text, text) TO {ROLE_DBA};",
f"GRANT execute ON FUNCTION set_user_u(text) TO {ROLE_DBA};"
f"GRANT execute ON FUNCTION reset_user() TO {ROLE_DBA};"
f"GRANT execute ON FUNCTION reset_user(text) TO {ROLE_DBA};"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the set_up_predefined_catalog_roles database function, so the right permissions are set to the charmed_dba role in every database.

Comment on lines 838 to 842
IF current_session_user LIKE 'relation-%' OR current_session_user LIKE 'relation_id_%' THEN
RAISE NOTICE 'Granting % to %', admin_user, current_session_user;
statement := 'GRANT ' || admin_user || ' TO "' || current_session_user || '";';
EXECUTE statement;
END IF;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the code is used to give the {database-name}_admin role to the relation user, so they can escalate to the {database-name}_owner user in a database that was created by themselves (through the charmed_databases_owner extra user role).

…into feature/16_predefined_roles_cleanup

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Signed-off-by: Marcelo Henrique Neppel <[email protected]>
…nup' into feature/16_predefined_catalog_roles

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Copy link
Contributor

@taurus-forever taurus-forever left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is LGTM, but the PostgreSQL roles itself requires extensive testing... I am not filling 100% confident in complex deployments (async, pgbouncer, etc).

Open for testing in 16/edge, LGTM for merging. Thank you for this incredible work done!!!

…ned_roles_cleanup

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
…nup' into feature/16_predefined_catalog_roles

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
Base automatically changed from feature/16_predefined_roles_cleanup to 16/edge June 8, 2025 12:31
…ned_catalog_roles

Signed-off-by: Marcelo Henrique Neppel <[email protected]>
@marceloneppel marceloneppel merged commit 577307e into 16/edge Jun 9, 2025
212 of 222 checks passed
@marceloneppel marceloneppel deleted the feature/16_predefined_catalog_roles branch June 9, 2025 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants