2828 PostgreSQLUndefinedPasswordError ,
2929 ROLE_DATABASES_OWNER ,
3030)
31+ from single_kernel_postgresql .config .literals import Substrates
3132
3233
3334@pytest .fixture (autouse = True )
@@ -341,9 +342,11 @@ def test_set_up_database_with_temp_tablespace_and_missing_owner_role(harness):
341342 patch ("single_kernel_postgresql.utils.postgresql.pwd.getpwuid" ) as _getpwuid ,
342343 ):
343344 # Simulate a temp location owned by wrong user/permissions to trigger fixup (33188 means 0o644)
344- stat_result = type ("stat_result" , (), {"st_uid" : 0 , "st_mode" : 33188 })
345+ stat_result = type ("stat_result" , (), {"st_uid" : 0 , "st_gid" : 0 , " st_mode" : 33188 })
345346 _stat .return_value = stat_result
346347 _getpwuid .return_value .pw_name = "root"
348+ _getpwuid .return_value .pw_uid = 0
349+ _getpwuid .return_value .pw_gid = 0
347350
348351 # First connection (non-context) for temp tablespace
349352 execute_direct = _connect_to_database .return_value .cursor .return_value .execute
@@ -403,9 +406,13 @@ def test_set_up_database_owner_mismatch_triggers_rename_and_fix(harness):
403406 patch ("single_kernel_postgresql.utils.postgresql.datetime" ) as _dt ,
404407 ):
405408 # Owner differs, permissions are correct (16832 means 0o700)
406- stat_result = type ("stat_result" , (), {"st_uid" : 0 , "st_mode" : 16832 })
409+ # Simulate directory owned by uid 1000 while expected owner has uid 0 to force mismatch
410+ stat_result = type ("stat_result" , (), {"st_uid" : 1000 , "st_gid" : 1000 , "st_mode" : 16832 })
407411 _stat .return_value = stat_result
412+ # The expected owner (SNAP_USER) resolves to uid 0/gid 0 for the test
408413 _getpwuid .return_value .pw_name = "root"
414+ _getpwuid .return_value .pw_uid = 0
415+ _getpwuid .return_value .pw_gid = 0
409416
410417 # Mock datetime.now(timezone.utc) to a fixed timestamp
411418 _dt .now .return_value = datetime (2025 , 1 , 1 , 1 , 2 , 3 , tzinfo = UTC )
@@ -439,9 +446,11 @@ def test_set_up_database_permissions_mismatch_triggers_rename_and_fix(harness):
439446 patch ("single_kernel_postgresql.utils.postgresql.datetime" ) as _dt ,
440447 ):
441448 # Owner matches SNAP_USER, permissions differ (33188 means 0o644)
442- stat_result = type ("stat_result" , (), {"st_uid" : 0 , "st_mode" : 33188 })
449+ stat_result = type ("stat_result" , (), {"st_uid" : 0 , "st_gid" : 0 , " st_mode" : 33188 })
443450 _stat .return_value = stat_result
444451 _getpwuid .return_value .pw_name = SNAP_USER
452+ _getpwuid .return_value .pw_uid = 0
453+ _getpwuid .return_value .pw_gid = 0
445454
446455 # Mock datetime.now(timezone.utc) to a fixed timestamp
447456 _dt .now .return_value = datetime (2025 , 1 , 1 , 1 , 2 , 3 , tzinfo = UTC )
@@ -497,7 +506,13 @@ def test_set_up_database_raises_wrapped_error(harness):
497506 ) as _connect_to_database ,
498507 patch ("single_kernel_postgresql.utils.postgresql.change_owner" ),
499508 patch ("single_kernel_postgresql.utils.postgresql.os.chmod" ),
509+ patch ("single_kernel_postgresql.utils.postgresql.pwd.getpwuid" ) as _getpwuid ,
500510 ):
511+ # Provide a dummy passwd entry so has_correct_ownership_and_permissions does not raise
512+ _getpwuid .return_value .pw_name = SNAP_USER
513+ _getpwuid .return_value .pw_uid = 0
514+ _getpwuid .return_value .pw_gid = 0
515+
501516 execute_direct = _connect_to_database .return_value .cursor .return_value .execute
502517 execute_direct .side_effect = psycopg2 .Error
503518 with pytest .raises (PostgreSQLDatabasesSetupError ):
@@ -506,17 +521,17 @@ def test_set_up_database_raises_wrapped_error(harness):
506521
507522def test_connect_to_database ():
508523 # Error on no host
509- pg = PostgreSQL (None , None , "operator" , None , "postgres" , None )
524+ pg = PostgreSQL (Substrates . VM , None , None , "operator" , None , "postgres" , None )
510525 with pytest .raises (PostgreSQLUndefinedHostError ):
511526 pg ._connect_to_database ()
512527
513528 # Error on no password
514- pg = PostgreSQL ("primary" , "current" , "operator" , None , "postgres" , None )
529+ pg = PostgreSQL (Substrates . VM , "primary" , "current" , "operator" , None , "postgres" , None )
515530 with pytest .raises (PostgreSQLUndefinedPasswordError ):
516531 pg ._connect_to_database ()
517532
518533 # Returns connection
519- pg = PostgreSQL ("primary" , "current" , "operator" , "password" , "postgres" , None )
534+ pg = PostgreSQL (Substrates . VM , "primary" , "current" , "operator" , "password" , "postgres" , None )
520535 with patch (
521536 "single_kernel_postgresql.utils.postgresql.psycopg2.connect" ,
522537 return_value = sentinel .connection ,
@@ -531,7 +546,9 @@ def test_is_user_in_hba():
531546 with patch (
532547 "single_kernel_postgresql.utils.postgresql.PostgreSQL._connect_to_database" ,
533548 ) as _connect_to_database :
534- pg = PostgreSQL ("primary" , "current" , "operator" , "password" , "postgres" , None )
549+ pg = PostgreSQL (
550+ Substrates .VM , "primary" , "current" , "operator" , "password" , "postgres" , None
551+ )
535552 _cursor = _connect_to_database ().__enter__ ().cursor ().__enter__ ()
536553
537554 # No result
@@ -562,7 +579,9 @@ def test_drop_hba_triggers():
562579 ) as _connect_to_database ,
563580 patch ("single_kernel_postgresql.utils.postgresql.logger" ) as _logger ,
564581 ):
565- pg = PostgreSQL ("primary" , "current" , "operator" , "password" , "postgres" , None )
582+ pg = PostgreSQL (
583+ Substrates .VM , "primary" , "current" , "operator" , "password" , "postgres" , None
584+ )
566585 _cursor = _connect_to_database ().__enter__ ().cursor ().__enter__ ()
567586 _cursor .fetchall .return_value = (("db1" ,), ("db2" ,))
568587
@@ -614,7 +633,9 @@ def test_create_user():
614633 "single_kernel_postgresql.utils.postgresql.PostgreSQL._process_extra_user_roles" ,
615634 ) as _process_extra_user_roles ,
616635 ):
617- pg = PostgreSQL ("primary" , "current" , "operator" , "password" , "postgres" , None )
636+ pg = PostgreSQL (
637+ Substrates .VM , "primary" , "current" , "operator" , "password" , "postgres" , None
638+ )
618639 _cursor = _connect_to_database ().__enter__ ().cursor ().__enter__ ()
619640 _process_extra_user_roles .return_value = (["role1" , "role2" ], ["priv1" , "priv2" ])
620641
@@ -731,9 +752,11 @@ def test_set_up_database_owner_and_permissions_match_no_rename_or_fix(harness):
731752 patch ("single_kernel_postgresql.utils.postgresql.pwd.getpwuid" ) as _getpwuid ,
732753 ):
733754 # Owner matches SNAP_USER and permissions are correct (16832 means 0o700)
734- stat_result = type ("stat_result" , (), {"st_uid" : 0 , "st_mode" : 16832 })
755+ stat_result = type ("stat_result" , (), {"st_uid" : 0 , "st_gid" : 0 , " st_mode" : 16832 })
735756 _stat .return_value = stat_result
736757 _getpwuid .return_value .pw_name = SNAP_USER
758+ _getpwuid .return_value .pw_uid = 0
759+ _getpwuid .return_value .pw_gid = 0
737760
738761 execute_direct = _connect_to_database .return_value .cursor .return_value .execute
739762 fetchone_direct = _connect_to_database .return_value .cursor .return_value .fetchone
@@ -753,3 +776,40 @@ def test_set_up_database_owner_and_permissions_match_no_rename_or_fix(harness):
753776 for c in execute_direct .call_args_list :
754777 if c .args :
755778 assert "ALTER TABLESPACE temp RENAME TO" not in c .args [0 ]
779+
780+
781+ def test_set_up_database_k8s_skips_change_owner_and_chmod (harness ):
782+ """When running on the K8S substrate, filesystem ownership/permission fixes should be skipped.
783+
784+ Even if the on-disk owner/permissions appear incorrect, change_owner and os.chmod must
785+ not be called for Substrates.K8S.
786+ """
787+ with (
788+ patch (
789+ "single_kernel_postgresql.utils.postgresql.PostgreSQL._connect_to_database"
790+ ) as _connect_to_database ,
791+ patch ("single_kernel_postgresql.utils.postgresql.PostgreSQL.set_up_login_hook_function" ),
792+ patch (
793+ "single_kernel_postgresql.utils.postgresql.PostgreSQL.set_up_predefined_catalog_roles_function"
794+ ),
795+ patch ("single_kernel_postgresql.utils.postgresql.change_owner" ) as _change_owner ,
796+ patch ("single_kernel_postgresql.utils.postgresql.os.chmod" ) as _chmod ,
797+ patch ("single_kernel_postgresql.utils.postgresql.os.stat" ) as _stat ,
798+ patch ("single_kernel_postgresql.utils.postgresql.pwd.getpwuid" ) as _getpwuid ,
799+ ):
800+ # Simulate a temp location owned by wrong user/permissions which would normally
801+ # trigger a fixup when running on VM substrate.
802+ stat_result = type ("stat_result" , (), {"st_uid" : 0 , "st_gid" : 0 , "st_mode" : 33188 })
803+ _stat .return_value = stat_result
804+ _getpwuid .return_value .pw_name = "root"
805+ _getpwuid .return_value .pw_uid = 0
806+ _getpwuid .return_value .pw_gid = 0
807+
808+ # Force the charm's PostgreSQL instance to think it's running on K8S.
809+ harness .charm .postgresql .substrate = Substrates .K8S
810+
811+ harness .charm .postgresql .set_up_database (temp_location = "/var/lib/postgresql/tmp" )
812+
813+ # On K8S substrate we must not attempt to change ownership or chmod the path.
814+ _change_owner .assert_not_called ()
815+ _chmod .assert_not_called ()
0 commit comments