Skip to content

Commit

Permalink
Select PostgresCluster by name for pgAdmin Server Group
Browse files Browse the repository at this point in the history
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
  • Loading branch information
tjmoore4 committed May 2, 2024
1 parent bc58063 commit c3fa600
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,10 @@ spec:
unique in the pgAdmin's ServerGroups since it becomes the
ServerGroup name in pgAdmin.
type: string
postgresClusterName:
description: PostgresClusterName selects one cluster to add
to pgAdmin by name.
type: string
postgresClusterSelector:
description: PostgresClusterSelector selects clusters to dynamically
add to pgAdmin by matching labels. An empty selector like
Expand Down Expand Up @@ -1417,8 +1421,11 @@ spec:
type: object
required:
- name
- postgresClusterSelector
type: object
x-kubernetes-validations:
- message: exactly one of "postgresClusterName" or "postgresClusterSelector"
is required
rule: '[has(self.postgresClusterName),has(self.postgresClusterSelector)].exists_one(x,x)'
type: array
serviceName:
description: ServiceName will be used as the name of a ClusterIP service
Expand Down
18 changes: 18 additions & 0 deletions internal/controller/standalone_pgadmin/postgrescluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"

"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

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

for _, serverGroup := range pgAdmin.Spec.ServerGroups {
cluster := &v1beta1.PostgresCluster{}
if serverGroup.PostgresClusterName != "" {
err = r.Get(ctx, types.NamespacedName{
Name: serverGroup.PostgresClusterName,
Namespace: pgAdmin.GetNamespace(),
}, cluster)
if err == nil {
matching[serverGroup.Name] = &v1beta1.PostgresClusterList{
Items: []v1beta1.PostgresCluster{*cluster},
}
}
continue
}
if selector, err = naming.AsSelector(serverGroup.PostgresClusterSelector); err == nil {
var filteredList v1beta1.PostgresClusterList
err = r.List(ctx, &filteredList,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,21 @@ type PGAdminSpec struct {
ServiceName string `json:"serviceName,omitempty"`
}

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

// PostgresClusterName selects one cluster to add to pgAdmin by name.
// +kubebuilder:validation:Optional
PostgresClusterName string `json:"postgresClusterName,omitempty"`

// PostgresClusterSelector selects clusters to dynamically add to pgAdmin by matching labels.
// An empty selector like `{}` will select ALL clusters in the namespace.
// +kubebuilder:validation:Required
PostgresClusterSelector metav1.LabelSelector `json:"postgresClusterSelector"`
// +kubebuilder:validation:Optional
PostgresClusterSelector metav1.LabelSelector `json:"postgresClusterSelector,omitempty"`
}

type PGAdminUser struct {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
# Check that invalid spec cannot be applied.
commands:
- script: |
contains() { bash -ceu '[[ "$1" == *"$2"* ]]' - "$@"; }
diff_comp() { bash -ceu 'diff <(echo "$1" ) <(echo "$2")' - "$@"; }
data_expected='"pgadmin2" is invalid: spec.serverGroups[0]: Invalid value: "object": exactly one of "postgresClusterName" or "postgresClusterSelector" is required'
data_actual=$(kubectl apply -f - 2>&1 <<EOF
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PGAdmin
metadata:
name: pgadmin2
spec:
dataVolumeClaimSpec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 1Gi
serverGroups:
- name: groupOne # can't have both a selector and name
postgresClusterSelector:
matchLabels:
hello: world
postgresClusterName: pgadmin4
EOF
)
{
contains "${data_actual}" "${data_expected}"
} || {
echo "Expected invalid error: got ${data_actual}"
diff_comp "${data_actual}" "${data_expected}"
exit 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- files/11-cluster.yaml
- files/11-pgadmin.yaml
error:
- files/11-pgadmin-check.yaml
80 changes: 80 additions & 0 deletions testing/kuttl/e2e-other/standalone-pgadmin-v8/12-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
apiVersion: kuttl.dev/v1beta1
kind: TestAssert
# Check the configmap is updated;
# Check the file is updated on the pod;
# Check the server dump is accurate.
# Because we have to wait for the configmap reload, make sure we have enough time.
timeout: 120
commands:
- script: |
contains() { bash -ceu '[[ "$1" == *"$2"* ]]' - "$@"; }
diff_comp() { bash -ceu 'diff <(echo "$1" ) <(echo "$2")' - "$@"; }
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"'
data_actual=$(kubectl get cm -l postgres-operator.crunchydata.com/pgadmin=pgadmin2 -n "${NAMESPACE}" -o json | jq .items[0].data)
{
contains "${data_actual}" "${data_expected}"
} || {
echo "Wrong configmap: got ${data_actual}"
diff_comp "${data_actual}" "${data_expected}"
exit 1
}
pod_name=$(kubectl get pod -n "${NAMESPACE}" -l postgres-operator.crunchydata.com/pgadmin=pgadmin2 -o name)
config_updated=$(kubectl exec -n "${NAMESPACE}" "${pod_name}" -- bash -c 'cat /etc/pgadmin/conf.d/~postgres-operator/pgadmin-shared-clusters.json')
config_expected='{
"Servers": {
"1": {
"Group": "groupOne",
"Host": "named-cluster-primary.'${NAMESPACE}.svc'",
"MaintenanceDB": "postgres",
"Name": "named-cluster",
"Port": 5432,
"SSLMode": "prefer",
"Shared": true,
"Username": "named-cluster"
}
}
}'
{
contains "${config_updated}" "${config_expected}"
} || {
echo "Wrong file mounted: got ${config_updated}"
echo "Wrong file mounted: expected ${config_expected}"
diff_comp "${config_updated}" "${config_expected}"
sleep 10
exit 1
}
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")
clusters_expected='
{
"Servers": {
"1": {
"Name": "named-cluster",
"Group": "groupOne",
"Host": "named-cluster-primary.'${NAMESPACE}.svc'",
"Port": 5432,
"MaintenanceDB": "postgres",
"Username": "named-cluster",
"Shared": true,
"TunnelPort": "22",
"KerberosAuthentication": false,
"ConnectionParameters": {
"sslmode": "prefer"
}
}
}
}'
{
contains "${clusters_actual}" "${clusters_expected}"
} || {
echo "Wrong servers dumped: got ${clusters_actual}"
echo "Wrong servers dumped: expected ${clusters_expected}"
diff_comp "${clusters_actual}" "${clusters_expected}"
exit 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: named-cluster
spec:
postgresVersion: ${KUTTL_PG_VERSION}
instances:
- name: instance1
dataVolumeClaimSpec: { accessModes: [ReadWriteOnce], resources: { requests: { storage: 1Gi } } }
backups:
pgbackrest:
repos:
- name: repo1
volume:
volumeClaimSpec: { accessModes: [ReadWriteOnce], resources: { requests: { storage: 1Gi } } }
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PGAdmin
metadata:
name: named-cluster
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PGAdmin
metadata:
name: pgadmin2
spec:
dataVolumeClaimSpec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 1Gi
serverGroups:
- name: groupOne
postgresClusterName: named-cluster

0 comments on commit c3fa600

Please sign in to comment.