Skip to content

Commit 5f98f86

Browse files
committed
Let mdb_admin manage resource groups
Add a contrib extension, pg_aux_catalog, exposing pg_create_mdb_admin_role(). Allow members of mdb_admin — not just superusers — to CREATE/ALTER/DROP resource groups and run pg_resgroup_move_query(), so a cloud admin can tune their own CPU/memory limits. Based on pg-sharding/cpg 7b8c912. Some tests are adapted from open-gpdb/gpdb 3ac99962ad2. Co-authored-by: reshke<reshke@double.cloud>
1 parent 2b3f662 commit 5f98f86

21 files changed

Lines changed: 628 additions & 23 deletions

.github/workflows/build-cloudberry-rocky8.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ jobs:
311311
"contrib/pgstattuple:installcheck",
312312
"contrib/tablefunc:installcheck",
313313
"contrib/passwordcheck:installcheck",
314+
"contrib/pg_aux_catalog:installcheck",
314315
"contrib/pg_buffercache:installcheck",
315316
"contrib/sslinfo:installcheck"]
316317
},

.github/workflows/build-cloudberry.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ jobs:
304304
"contrib/pgstattuple:installcheck",
305305
"contrib/tablefunc:installcheck",
306306
"contrib/passwordcheck:installcheck",
307+
"contrib/pg_aux_catalog:installcheck",
307308
"contrib/pg_buffercache:installcheck",
308309
"contrib/sslinfo:installcheck"]
309310
},

.github/workflows/build-deb-cloudberry.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ jobs:
243243
"contrib/pgstattuple:installcheck",
244244
"contrib/tablefunc:installcheck",
245245
"contrib/passwordcheck:installcheck",
246+
"contrib/pg_aux_catalog:installcheck",
246247
"contrib/pg_buffercache:installcheck",
247248
"contrib/sslinfo:installcheck"]
248249
},

contrib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ SUBDIRS = \
3434
old_snapshot \
3535
pageinspect \
3636
passwordcheck \
37+
pg_aux_catalog \
3738
postgres_fdw \
3839
pg_buffercache \
3940
pg_freespacemap \

contrib/pg_aux_catalog/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Generated test output
2+
/log/
3+
/results/
4+
/tmp_check/
5+
/isolation2/results/
6+
/isolation2/output_iso/

contrib/pg_aux_catalog/Makefile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# contrib/pg_aux_catalog/Makefile
2+
3+
MODULE_big = pg_aux_catalog
4+
OBJS = \
5+
$(WIN32RES) \
6+
pg_aux_catalog.o
7+
8+
EXTENSION = pg_aux_catalog
9+
DATA = pg_aux_catalog--1.0.sql
10+
11+
PGFILEDESC = "pg_aux_catalog - auxiliary catalog management"
12+
13+
REGRESS = pg_aux_catalog
14+
REGRESS_OPTS = --init-file=$(top_srcdir)/src/test/regress/init_file
15+
16+
ifdef USE_PGXS
17+
PG_CONFIG = pg_config
18+
PGXS := $(shell $(PG_CONFIG) --pgxs)
19+
include $(PGXS)
20+
else
21+
subdir = contrib/pg_aux_catalog
22+
top_builddir = ../..
23+
include $(top_builddir)/src/Makefile.global
24+
include $(top_srcdir)/contrib/contrib-global.mk
25+
endif
26+
27+
# Multi-session resource-group permission tests, run with the GPDB isolation2
28+
# harness. Requires a running cluster with resource groups enabled
29+
# (gp_resource_manager=group); see installcheck-resgroup in
30+
# src/test/isolation2. Not part of the default "installcheck".
31+
installcheck-isolation2: install
32+
$(pg_isolation2_regress_installcheck) \
33+
--init-file=$(top_builddir)/src/test/regress/init_file \
34+
--inputdir=$(srcdir)/isolation2 \
35+
--outputdir=isolation2 \
36+
--schedule=$(srcdir)/isolation2/isolation2_schedule
37+

contrib/pg_aux_catalog/README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# pg_aux_catalog
2+
3+
Auxiliary catalog management for Apache Cloudberry.
4+
5+
This extension provisions the **`mdb_admin`** privilege role, which lets a
6+
non-superuser manage resource groups in managed-service deployments where the
7+
client is never given superuser.
8+
9+
## Background
10+
11+
In Greenplum/Cloudberry only a superuser may `CREATE`/`ALTER`/`DROP` resource
12+
groups or move a running query between groups with `pg_resgroup_move_query()`.
13+
The server gates those four entry points on membership of `mdb_admin`,
14+
identified by a **fixed OID (8067)** rather than by name, so the privilege is
15+
recognised reliably across the coordinator and all segments.
16+
17+
A fixed OID cannot be obtained from a plain `CREATE ROLE` (that assigns an
18+
ordinary OID). This extension provides the one supported way to create the
19+
role at OID 8067.
20+
21+
## Functions
22+
23+
### `pg_create_mdb_admin_role() returns oid`
24+
25+
Creates the `mdb_admin` role with its fixed OID (8067).
26+
Returns the OID of the created role (8067). Errors if a role with that OID or
27+
the name `mdb_admin` already exists. The OID assignment is dispatched to the
28+
segments, so the role has the same OID cluster-wide.
29+
30+
## Usage
31+
32+
```sql
33+
CREATE EXTENSION pg_aux_catalog;
34+
35+
-- Provision the role (the control plane does this once per cluster).
36+
SELECT pg_create_mdb_admin_role();
37+
38+
-- Grant the capability to a tenant admin.
39+
GRANT mdb_admin TO cloud_admin;
40+
41+
-- cloud_admin can now manage resource groups without superuser:
42+
SET ROLE cloud_admin;
43+
CREATE RESOURCE GROUP rg_tenant WITH (concurrency = 4, cpu_max_percent = 20);
44+
ALTER RESOURCE GROUP rg_tenant SET cpu_max_percent 30;
45+
DROP RESOURCE GROUP rg_tenant;
46+
```
47+
48+
`admin_group` and `system_group` remain superuser-only for `ALTER`/`DROP`:
49+
they are infrastructure, not user-tunable groups.
50+
51+
## Building and testing
52+
53+
```sh
54+
make -C contrib/pg_aux_catalog install
55+
make -C contrib/pg_aux_catalog installcheck
56+
```
57+
58+
`installcheck` runs a single-session regression test (role creation and the
59+
resource-group permission gate). A multi-session isolation2 test covering the
60+
dispatched / cross-session behaviour lives under `isolation2/` and is run
61+
separately, against a cluster with resource groups enabled
62+
(`gp_resource_manager=group`):
63+
64+
```sh
65+
make -C contrib/pg_aux_catalog installcheck-isolation2
66+
```
67+
68+
## Credits
69+
70+
Based on [pg-sharding/cpg](https://github.com/pg-sharding/cpg) commit
71+
`7b8c912`. Some tests are adapted from open-gpdb/gpdb commit `3ac99962ad2`.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
-- Tests for the pg_aux_catalog extension: creation of the fixed-OID
2+
-- mdb_admin role and the resource-group permission gate it enables.
3+
CREATE EXTENSION pg_aux_catalog;
4+
-- ---------------------------------------------------------------------
5+
-- pg_create_mdb_admin_role() creates the mdb_admin role with its fixed OID.
6+
-- ---------------------------------------------------------------------
7+
SELECT pg_create_mdb_admin_role() AS mdb_admin_oid;
8+
mdb_admin_oid
9+
---------------
10+
8067
11+
(1 row)
12+
13+
-- The role exists with the fixed OID and is a non-login, non-superuser,
14+
-- connection-limited role.
15+
SELECT oid = 8067 AS has_fixed_oid, rolcanlogin, rolsuper,
16+
rolcreaterole, rolcreatedb, rolconnlimit
17+
FROM pg_authid WHERE rolname = 'mdb_admin';
18+
has_fixed_oid | rolcanlogin | rolsuper | rolcreaterole | rolcreatedb | rolconnlimit
19+
---------------+-------------+----------+---------------+-------------+--------------
20+
t | f | f | f | f | 0
21+
(1 row)
22+
23+
-- Creating it a second time is rejected.
24+
SELECT pg_create_mdb_admin_role();
25+
ERROR: role with OID 8067 already exists
26+
-- ---------------------------------------------------------------------
27+
-- Resource-group permission gate: a role that is not a member of mdb_admin
28+
-- is rejected on every entry point. These checks run before the "resource
29+
-- group is enabled" check, so they are deterministic regardless of the
30+
-- resource manager in use.
31+
-- ---------------------------------------------------------------------
32+
CREATE ROLE regress_rg_noadmin;
33+
SET ROLE regress_rg_noadmin;
34+
CREATE RESOURCE GROUP regress_rg_x WITH (concurrency=1, cpu_max_percent=5);
35+
ERROR: must be mdb_admin to create resource groups
36+
ALTER RESOURCE GROUP regress_rg_x SET cpu_max_percent 6;
37+
ERROR: must be mdb_admin to alter resource groups
38+
DROP RESOURCE GROUP regress_rg_x;
39+
ERROR: must be mdb_admin to drop resource groups
40+
RESET ROLE;
41+
DROP ROLE regress_rg_noadmin;
42+
-- ---------------------------------------------------------------------
43+
-- Cleanup.
44+
-- ---------------------------------------------------------------------
45+
DROP ROLE mdb_admin;
46+
DROP EXTENSION pg_aux_catalog;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
-- Tests permission checks for the mdb_admin role with
2+
-- resource groups enabled.
3+
4+
-- start_matchsubs
5+
-- m/ERROR: cannot find process: \d+/
6+
-- s/\d+/XXX/g
7+
-- end_matchsubs
8+
9+
DROP ROLE IF EXISTS role_rg_admin;
10+
DROP
11+
DROP ROLE IF EXISTS role_rg_noadmin;
12+
DROP
13+
DROP ROLE IF EXISTS mdb_admin;
14+
DROP
15+
-- start_ignore
16+
DROP RESOURCE GROUP rg_perm_admin1;
17+
DROP RESOURCE GROUP rg_perm_admin2;
18+
DROP RESOURCE GROUP rg_perm_revoke1;
19+
DROP RESOURCE GROUP rg_perm_revoke2;
20+
DROP RESOURCE GROUP rg_perm_test;
21+
-- end_ignore
22+
23+
-- ---------------------------------------------------------------------
24+
-- Setup. The mdb_admin role is not predefined in the catalog; it is
25+
-- created here the same way the control plane provisions it at runtime.
26+
-- ---------------------------------------------------------------------
27+
CREATE RESOURCE GROUP rg_perm_test WITH (concurrency=2, cpu_max_percent=10);
28+
CREATE
29+
CREATE ROLE mdb_admin;
30+
CREATE
31+
CREATE ROLE role_rg_admin RESOURCE GROUP rg_perm_test;
32+
CREATE
33+
CREATE ROLE role_rg_noadmin RESOURCE GROUP rg_perm_test;
34+
CREATE
35+
GRANT mdb_admin TO role_rg_admin;
36+
GRANT
37+
38+
-- ---------------------------------------------------------------------
39+
-- 1. Member of mdb_admin can CREATE/ALTER/DROP resource groups
40+
-- (statements are dispatched to segments).
41+
-- ---------------------------------------------------------------------
42+
1: SET ROLE role_rg_admin;
43+
SET
44+
1: CREATE RESOURCE GROUP rg_perm_admin1 WITH (concurrency=1, cpu_max_percent=5);
45+
CREATE
46+
1: ALTER RESOURCE GROUP rg_perm_admin1 SET cpu_max_percent 6;
47+
ALTER
48+
1: DROP RESOURCE GROUP rg_perm_admin1;
49+
DROP
50+
51+
-- 2. Even a member cannot ALTER or DROP the system admin_group.
52+
1: ALTER RESOURCE GROUP admin_group SET cpu_max_percent 99;
53+
ERROR: must be superuser to alter resource group "admin_group"
54+
1: DROP RESOURCE GROUP admin_group;
55+
ERROR: must be superuser to drop resource group "admin_group"
56+
1q: ... <quitting>
57+
58+
-- ---------------------------------------------------------------------
59+
-- 3. A non-member is rejected on every entry point.
60+
-- ---------------------------------------------------------------------
61+
2: SET ROLE role_rg_noadmin;
62+
SET
63+
2: CREATE RESOURCE GROUP rg_perm_admin2 WITH (concurrency=1, cpu_max_percent=5);
64+
ERROR: must be mdb_admin to create resource groups
65+
2: ALTER RESOURCE GROUP rg_perm_test SET cpu_max_percent 7;
66+
ERROR: must be mdb_admin to alter resource groups
67+
2: DROP RESOURCE GROUP rg_perm_test;
68+
ERROR: must be mdb_admin to drop resource groups
69+
2q: ... <quitting>
70+
71+
-- ---------------------------------------------------------------------
72+
-- 4. pg_resgroup_move_query() honours the same permission check.
73+
-- The first call (non-member) must fail with "must be mdb_admin".
74+
-- The second call (member) gets past the permission gate and
75+
-- fails on the pid lookup (masked by start_matchsubs above).
76+
-- ---------------------------------------------------------------------
77+
3: SET ROLE role_rg_noadmin;
78+
SET
79+
3: SELECT pg_resgroup_move_query(999999999, 'admin_group');
80+
ERROR: must be mdb_admin to move query
81+
3: RESET ROLE;
82+
RESET
83+
3: SET ROLE role_rg_admin;
84+
SET
85+
3: SELECT pg_resgroup_move_query(999999999, 'admin_group');
86+
ERROR: cannot find process: XXX
87+
3q: ... <quitting>
88+
89+
-- ---------------------------------------------------------------------
90+
-- 5. Cross-session REVOKE takes effect on the granted session's
91+
-- next statement (the privilege is re-checked per command, not
92+
-- cached at SET ROLE time).
93+
-- ---------------------------------------------------------------------
94+
4: SET ROLE role_rg_admin;
95+
SET
96+
4: CREATE RESOURCE GROUP rg_perm_revoke1 WITH (concurrency=1, cpu_max_percent=5);
97+
CREATE
98+
5: REVOKE mdb_admin FROM role_rg_admin;
99+
REVOKE
100+
4: CREATE RESOURCE GROUP rg_perm_revoke2 WITH (concurrency=1, cpu_max_percent=5);
101+
ERROR: must be mdb_admin to create resource groups
102+
4: DROP RESOURCE GROUP rg_perm_revoke1;
103+
ERROR: must be mdb_admin to drop resource groups
104+
4q: ... <quitting>
105+
5q: ... <quitting>
106+
107+
-- ---------------------------------------------------------------------
108+
-- Cleanup. Roles must be dropped before the resource group they
109+
-- reference, otherwise DROP RESOURCE GROUP fails with
110+
-- "resource group is used by at least one role".
111+
-- ---------------------------------------------------------------------
112+
RESET ROLE;
113+
RESET
114+
DROP ROLE role_rg_admin;
115+
DROP
116+
DROP ROLE role_rg_noadmin;
117+
DROP
118+
DROP ROLE mdb_admin;
119+
DROP
120+
DROP RESOURCE GROUP rg_perm_revoke1;
121+
DROP
122+
DROP RESOURCE GROUP rg_perm_test;
123+
DROP
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test: resgroup_mdb_admin

0 commit comments

Comments
 (0)