Skip to content

Commit c3fa600

Browse files
committed
Select PostgresCluster by name for pgAdmin Server Group
This change adds the ability to select a PostgresCluster by name for a given pgAdmin ServerGroup in addition to selecting by label. Issue: PGO-1075
1 parent bc58063 commit c3fa600

File tree

9 files changed

+190
-3
lines changed

9 files changed

+190
-3
lines changed

config/crd/bases/postgres-operator.crunchydata.com_pgadmins.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,10 @@ spec:
13691369
unique in the pgAdmin's ServerGroups since it becomes the
13701370
ServerGroup name in pgAdmin.
13711371
type: string
1372+
postgresClusterName:
1373+
description: PostgresClusterName selects one cluster to add
1374+
to pgAdmin by name.
1375+
type: string
13721376
postgresClusterSelector:
13731377
description: PostgresClusterSelector selects clusters to dynamically
13741378
add to pgAdmin by matching labels. An empty selector like
@@ -1417,8 +1421,11 @@ spec:
14171421
type: object
14181422
required:
14191423
- name
1420-
- postgresClusterSelector
14211424
type: object
1425+
x-kubernetes-validations:
1426+
- message: exactly one of "postgresClusterName" or "postgresClusterSelector"
1427+
is required
1428+
rule: '[has(self.postgresClusterName),has(self.postgresClusterSelector)].exists_one(x,x)'
14221429
type: array
14231430
serviceName:
14241431
description: ServiceName will be used as the name of a ClusterIP service

internal/controller/standalone_pgadmin/postgrescluster.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
2222

2323
"k8s.io/apimachinery/pkg/labels"
24+
"k8s.io/apimachinery/pkg/types"
2425
"sigs.k8s.io/controller-runtime/pkg/client"
2526
)
2627

@@ -44,6 +45,10 @@ func (r *PGAdminReconciler) findPGAdminsForPostgresCluster(
4445
}) == nil {
4546
for i := range pgadmins.Items {
4647
for _, serverGroup := range pgadmins.Items[i].Spec.ServerGroups {
48+
if serverGroup.PostgresClusterName == cluster.GetName() {
49+
matching = append(matching, &pgadmins.Items[i])
50+
continue
51+
}
4752
if selector, err := naming.AsSelector(serverGroup.PostgresClusterSelector); err == nil {
4853
if selector.Matches(labels.Set(cluster.GetLabels())) {
4954
matching = append(matching, &pgadmins.Items[i])
@@ -67,6 +72,19 @@ func (r *PGAdminReconciler) getClustersForPGAdmin(
6772
var selector labels.Selector
6873

6974
for _, serverGroup := range pgAdmin.Spec.ServerGroups {
75+
cluster := &v1beta1.PostgresCluster{}
76+
if serverGroup.PostgresClusterName != "" {
77+
err = r.Get(ctx, types.NamespacedName{
78+
Name: serverGroup.PostgresClusterName,
79+
Namespace: pgAdmin.GetNamespace(),
80+
}, cluster)
81+
if err == nil {
82+
matching[serverGroup.Name] = &v1beta1.PostgresClusterList{
83+
Items: []v1beta1.PostgresCluster{*cluster},
84+
}
85+
}
86+
continue
87+
}
7088
if selector, err = naming.AsSelector(serverGroup.PostgresClusterSelector); err == nil {
7189
var filteredList v1beta1.PostgresClusterList
7290
err = r.List(ctx, &filteredList,

pkg/apis/postgres-operator.crunchydata.com/v1beta1/standalone_pgadmin_types.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,21 @@ type PGAdminSpec struct {
132132
ServiceName string `json:"serviceName,omitempty"`
133133
}
134134

135+
// +kubebuilder:validation:XValidation:rule=`[has(self.postgresClusterName),has(self.postgresClusterSelector)].exists_one(x,x)`,message=`exactly one of "postgresClusterName" or "postgresClusterSelector" is required`
135136
type ServerGroup struct {
136137
// The name for the ServerGroup in pgAdmin.
137138
// Must be unique in the pgAdmin's ServerGroups since it becomes the ServerGroup name in pgAdmin.
138139
// +kubebuilder:validation:Required
139140
Name string `json:"name"`
140141

142+
// PostgresClusterName selects one cluster to add to pgAdmin by name.
143+
// +kubebuilder:validation:Optional
144+
PostgresClusterName string `json:"postgresClusterName,omitempty"`
145+
141146
// PostgresClusterSelector selects clusters to dynamically add to pgAdmin by matching labels.
142147
// An empty selector like `{}` will select ALL clusters in the namespace.
143-
// +kubebuilder:validation:Required
144-
PostgresClusterSelector metav1.LabelSelector `json:"postgresClusterSelector"`
148+
// +kubebuilder:validation:Optional
149+
PostgresClusterSelector metav1.LabelSelector `json:"postgresClusterSelector,omitempty"`
145150
}
146151

147152
type PGAdminUser struct {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
apiVersion: kuttl.dev/v1beta1
2+
kind: TestStep
3+
# Check that invalid spec cannot be applied.
4+
commands:
5+
- script: |
6+
contains() { bash -ceu '[[ "$1" == *"$2"* ]]' - "$@"; }
7+
diff_comp() { bash -ceu 'diff <(echo "$1" ) <(echo "$2")' - "$@"; }
8+
9+
data_expected='"pgadmin2" is invalid: spec.serverGroups[0]: Invalid value: "object": exactly one of "postgresClusterName" or "postgresClusterSelector" is required'
10+
11+
data_actual=$(kubectl apply -f - 2>&1 <<EOF
12+
apiVersion: postgres-operator.crunchydata.com/v1beta1
13+
kind: PGAdmin
14+
metadata:
15+
name: pgadmin2
16+
spec:
17+
dataVolumeClaimSpec:
18+
accessModes:
19+
- "ReadWriteOnce"
20+
resources:
21+
requests:
22+
storage: 1Gi
23+
serverGroups:
24+
- name: groupOne # can't have both a selector and name
25+
postgresClusterSelector:
26+
matchLabels:
27+
hello: world
28+
postgresClusterName: pgadmin4
29+
EOF
30+
)
31+
{
32+
contains "${data_actual}" "${data_expected}"
33+
} || {
34+
echo "Expected invalid error: got ${data_actual}"
35+
diff_comp "${data_actual}" "${data_expected}"
36+
exit 1
37+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: kuttl.dev/v1beta1
2+
kind: TestStep
3+
apply:
4+
- files/11-cluster.yaml
5+
- files/11-pgadmin.yaml
6+
error:
7+
- files/11-pgadmin-check.yaml
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
apiVersion: kuttl.dev/v1beta1
2+
kind: TestAssert
3+
# Check the configmap is updated;
4+
# Check the file is updated on the pod;
5+
# Check the server dump is accurate.
6+
# Because we have to wait for the configmap reload, make sure we have enough time.
7+
timeout: 120
8+
commands:
9+
- script: |
10+
contains() { bash -ceu '[[ "$1" == *"$2"* ]]' - "$@"; }
11+
diff_comp() { bash -ceu 'diff <(echo "$1" ) <(echo "$2")' - "$@"; }
12+
13+
data_expected='"pgadmin-shared-clusters.json": "{\n \"Servers\": {\n \"1\": {\n \"Group\": \"groupOne\",\n \"Host\": \"named-cluster-primary.'${NAMESPACE}.svc'\",\n \"MaintenanceDB\": \"postgres\",\n \"Name\": \"named-cluster\",\n \"Port\": 5432,\n \"SSLMode\": \"prefer\",\n \"Shared\": true,\n \"Username\": \"named-cluster\"\n }\n }\n}\n"'
14+
15+
data_actual=$(kubectl get cm -l postgres-operator.crunchydata.com/pgadmin=pgadmin2 -n "${NAMESPACE}" -o json | jq .items[0].data)
16+
17+
{
18+
contains "${data_actual}" "${data_expected}"
19+
} || {
20+
echo "Wrong configmap: got ${data_actual}"
21+
diff_comp "${data_actual}" "${data_expected}"
22+
exit 1
23+
}
24+
25+
pod_name=$(kubectl get pod -n "${NAMESPACE}" -l postgres-operator.crunchydata.com/pgadmin=pgadmin2 -o name)
26+
27+
config_updated=$(kubectl exec -n "${NAMESPACE}" "${pod_name}" -- bash -c 'cat /etc/pgadmin/conf.d/~postgres-operator/pgadmin-shared-clusters.json')
28+
config_expected='{
29+
"Servers": {
30+
"1": {
31+
"Group": "groupOne",
32+
"Host": "named-cluster-primary.'${NAMESPACE}.svc'",
33+
"MaintenanceDB": "postgres",
34+
"Name": "named-cluster",
35+
"Port": 5432,
36+
"SSLMode": "prefer",
37+
"Shared": true,
38+
"Username": "named-cluster"
39+
}
40+
}
41+
}'
42+
{
43+
contains "${config_updated}" "${config_expected}"
44+
} || {
45+
echo "Wrong file mounted: got ${config_updated}"
46+
echo "Wrong file mounted: expected ${config_expected}"
47+
diff_comp "${config_updated}" "${config_expected}"
48+
sleep 10
49+
exit 1
50+
}
51+
52+
clusters_actual=$(kubectl exec -n "${NAMESPACE}" "${pod_name}" -- bash -c "python3 /usr/local/lib/python3.11/site-packages/pgadmin4/setup.py dump-servers /tmp/dumped.json --user admin@pgadmin2.${NAMESPACE}.svc && cat /tmp/dumped.json")
53+
54+
clusters_expected='
55+
{
56+
"Servers": {
57+
"1": {
58+
"Name": "named-cluster",
59+
"Group": "groupOne",
60+
"Host": "named-cluster-primary.'${NAMESPACE}.svc'",
61+
"Port": 5432,
62+
"MaintenanceDB": "postgres",
63+
"Username": "named-cluster",
64+
"Shared": true,
65+
"TunnelPort": "22",
66+
"KerberosAuthentication": false,
67+
"ConnectionParameters": {
68+
"sslmode": "prefer"
69+
}
70+
}
71+
}
72+
}'
73+
{
74+
contains "${clusters_actual}" "${clusters_expected}"
75+
} || {
76+
echo "Wrong servers dumped: got ${clusters_actual}"
77+
echo "Wrong servers dumped: expected ${clusters_expected}"
78+
diff_comp "${clusters_actual}" "${clusters_expected}"
79+
exit 1
80+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: postgres-operator.crunchydata.com/v1beta1
2+
kind: PostgresCluster
3+
metadata:
4+
name: named-cluster
5+
spec:
6+
postgresVersion: ${KUTTL_PG_VERSION}
7+
instances:
8+
- name: instance1
9+
dataVolumeClaimSpec: { accessModes: [ReadWriteOnce], resources: { requests: { storage: 1Gi } } }
10+
backups:
11+
pgbackrest:
12+
repos:
13+
- name: repo1
14+
volume:
15+
volumeClaimSpec: { accessModes: [ReadWriteOnce], resources: { requests: { storage: 1Gi } } }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
apiVersion: postgres-operator.crunchydata.com/v1beta1
2+
kind: PGAdmin
3+
metadata:
4+
name: named-cluster
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: postgres-operator.crunchydata.com/v1beta1
2+
kind: PGAdmin
3+
metadata:
4+
name: pgadmin2
5+
spec:
6+
dataVolumeClaimSpec:
7+
accessModes:
8+
- "ReadWriteOnce"
9+
resources:
10+
requests:
11+
storage: 1Gi
12+
serverGroups:
13+
- name: groupOne
14+
postgresClusterName: named-cluster

0 commit comments

Comments
 (0)