Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions config/crd/bases/postgres-operator.crunchydata.com_pgadmins.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2621,6 +2621,57 @@ spec:
x-kubernetes-list-map-keys:
- username
x-kubernetes-list-type: map
volumes:
description: PGAdminVolumesSpec defines the configuration for pgAdmin
additional volumes
properties:
additional:
description: Additional pre-existing volumes to add to the pod.
items:
properties:
claimName:
description: Name of an existing PersistentVolumeClaim.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
containers:
description: |-
The names of containers in which to mount this volume.
The default mounts the volume in *all* containers. An empty list does not mount the volume to any containers.
items:
maxLength: 63
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
maxItems: 10
type: array
x-kubernetes-list-type: set
name:
allOf:
- maxLength: 63
- maxLength: 55
description: |-
The name of the directory in which to mount this volume.
Volumes are mounted in containers at `/volumes/{name}`.
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
readOnly:
description: When true, mount the volume read-only, otherwise
read-write. Defaults to false.
type: boolean
required:
- claimName
- name
type: object
x-kubernetes-map-type: atomic
maxItems: 10
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
type: object
required:
- dataVolumeClaimSpec
type: object
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/postgrescluster/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -1196,7 +1196,7 @@ func (r *Reconciler) reconcileInstance(

// mount additional volumes to the Postgres instance containers
if err == nil && spec.Volumes != nil && len(spec.Volumes.Additional) > 0 {
missingContainers := addAdditionalVolumesToSpecifiedContainers(&instance.Spec.Template, spec.Volumes.Additional)
missingContainers := AddAdditionalVolumesToSpecifiedContainers(&instance.Spec.Template, spec.Volumes.Additional)

if len(missingContainers) > 0 {
r.Recorder.Eventf(cluster, corev1.EventTypeWarning, "SpecifiedContainerNotFound",
Expand Down
6 changes: 3 additions & 3 deletions internal/controller/postgrescluster/pgbackrest.go
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ func (r *Reconciler) generateRepoHostIntent(ctx context.Context, postgresCluster

// mount additional volumes to the repo host containers
if repoHost != nil && repoHost.Volumes != nil && len(repoHost.Volumes.Additional) > 0 {
missingContainers := addAdditionalVolumesToSpecifiedContainers(&repo.Spec.Template, repoHost.Volumes.Additional)
missingContainers := AddAdditionalVolumesToSpecifiedContainers(&repo.Spec.Template, repoHost.Volumes.Additional)

if len(missingContainers) > 0 {
r.Recorder.Eventf(postgresCluster, corev1.EventTypeWarning, "SpecifiedContainerNotFound",
Expand Down Expand Up @@ -908,7 +908,7 @@ func (r *Reconciler) generateBackupJobSpecIntent(ctx context.Context, postgresCl

// mount additional volumes to the job containers
if jobs != nil && jobs.Volumes != nil && len(jobs.Volumes.Additional) > 0 {
missingContainers := addAdditionalVolumesToSpecifiedContainers(&jobSpec.Template, jobs.Volumes.Additional)
missingContainers := AddAdditionalVolumesToSpecifiedContainers(&jobSpec.Template, jobs.Volumes.Additional)

if len(missingContainers) > 0 {
r.Recorder.Eventf(postgresCluster, corev1.EventTypeWarning, "SpecifiedContainerNotFound",
Expand Down Expand Up @@ -1408,7 +1408,7 @@ func (r *Reconciler) generateRestoreJobIntent(cluster *v1beta1.PostgresCluster,
job.Spec.Template.Spec.PriorityClassName = initialize.FromPointer(dataSource.PriorityClassName)

if dataSource.Volumes != nil && len(dataSource.Volumes.Additional) > 0 {
missingContainers := addAdditionalVolumesToSpecifiedContainers(&job.Spec.Template, dataSource.Volumes.Additional)
missingContainers := AddAdditionalVolumesToSpecifiedContainers(&job.Spec.Template, dataSource.Volumes.Additional)

if len(missingContainers) > 0 {
r.Recorder.Eventf(cluster, corev1.EventTypeWarning, "SpecifiedContainerNotFound",
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/postgrescluster/pgbouncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ func (r *Reconciler) generatePGBouncerDeployment(

// mount additional volumes to the pgbouncer containers
if err == nil && cluster.Spec.Proxy.PGBouncer.Volumes != nil && len(cluster.Spec.Proxy.PGBouncer.Volumes.Additional) > 0 {
missingContainers := addAdditionalVolumesToSpecifiedContainers(&deploy.Spec.Template, cluster.Spec.Proxy.PGBouncer.Volumes.Additional)
missingContainers := AddAdditionalVolumesToSpecifiedContainers(&deploy.Spec.Template, cluster.Spec.Proxy.PGBouncer.Volumes.Additional)

if len(missingContainers) > 0 {
r.Recorder.Eventf(cluster, corev1.EventTypeWarning, "SpecifiedContainerNotFound",
Expand Down
6 changes: 3 additions & 3 deletions internal/controller/postgrescluster/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,12 @@ func AdditionalVolumeMount(name string, readOnly bool) corev1.VolumeMount {
}
}

// addAdditionalVolumesToSpecifiedContainers adds additional volumes to the specified
// AddAdditionalVolumesToSpecifiedContainers adds additional volumes to the specified
// containers in the specified pod
// addAdditionalVolumesToSpecifiedContainers adds the volumes to the pod
// AddAdditionalVolumesToSpecifiedContainers adds the volumes to the pod
// as `volumes-<additionalVolumeRequest.Name>`
// and adds the directory to the path `/volumes/<additionalVolumeRequest.Name>`
func addAdditionalVolumesToSpecifiedContainers(template *corev1.PodTemplateSpec,
func AddAdditionalVolumesToSpecifiedContainers(template *corev1.PodTemplateSpec,
additionalVolumes []v1beta1.AdditionalVolume) []string {

missingContainers := []string{}
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/postgrescluster/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ func TestAddAdditionalVolumesToSpecifiedContainers(t *testing.T) {

copyPodTemplate := podTemplate.DeepCopy()

missingContainers := addAdditionalVolumesToSpecifiedContainers(
missingContainers := AddAdditionalVolumesToSpecifiedContainers(
copyPodTemplate,
tc.additionalVolumes,
)
Expand Down
14 changes: 12 additions & 2 deletions internal/controller/standalone_pgadmin/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (r *PGAdminReconciler) reconcilePGAdminStatefulSet(
ctx context.Context, pgadmin *v1beta1.PGAdmin,
configmap *corev1.ConfigMap, dataVolume *corev1.PersistentVolumeClaim,
) error {
sts := statefulset(ctx, pgadmin, configmap, dataVolume)
sts := r.statefulset(ctx, pgadmin, configmap, dataVolume)

// Previous versions of PGO used a StatefulSet Pod Management Policy that could leave the Pod
// in a failed state. When we see that it has the wrong policy, we will delete the StatefulSet
Expand Down Expand Up @@ -58,7 +58,7 @@ func (r *PGAdminReconciler) reconcilePGAdminStatefulSet(
}

// statefulset defines the StatefulSet needed to run pgAdmin.
func statefulset(
func (r *PGAdminReconciler) statefulset(
ctx context.Context,
pgadmin *v1beta1.PGAdmin,
configmap *corev1.ConfigMap,
Expand Down Expand Up @@ -138,5 +138,15 @@ func statefulset(

postgrescluster.AddTMPEmptyDir(&sts.Spec.Template)

// mount additional volumes to the Postgres instance containers
if pgadmin.Spec.Volumes != nil && len(pgadmin.Spec.Volumes.Additional) > 0 {
missingContainers := postgrescluster.AddAdditionalVolumesToSpecifiedContainers(&sts.Spec.Template, pgadmin.Spec.Volumes.Additional)

if len(missingContainers) > 0 {
r.Recorder.Eventf(pgadmin, corev1.EventTypeWarning, "SpecifiedContainerNotFound",
"The following pgAdmin pod containers were specified for additional volumes but cannot be found: %s.", missingContainers)
}
}

return sts
}
76 changes: 76 additions & 0 deletions internal/controller/standalone_pgadmin/statefulset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/crunchydata/postgres-operator/internal/controller/runtime"
"github.com/crunchydata/postgres-operator/internal/initialize"
"github.com/crunchydata/postgres-operator/internal/naming"
"github.com/crunchydata/postgres-operator/internal/testing/cmp"
"github.com/crunchydata/postgres-operator/internal/testing/events"
"github.com/crunchydata/postgres-operator/internal/testing/require"
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
)
Expand Down Expand Up @@ -219,4 +221,78 @@ tolerations:

assert.Assert(t, cmp.MarshalMatches(template.Spec, compare))
})

t.Run("verify additional volumes", func(t *testing.T) {
recorder := events.NewRecorder(t, runtime.Scheme)
reconciler.Recorder = recorder

custompgadmin := new(v1beta1.PGAdmin)

// add pod level customizations
custompgadmin.Name = "custom-volumes"
custompgadmin.Namespace = ns.Name

require.UnmarshalInto(t, &custompgadmin.Spec, `{
dataVolumeClaimSpec: {
accessModes: [ReadWriteOnce],
resources: { requests: { storage: 1Gi } },
},
}`)
require.UnmarshalInto(t, &custompgadmin.Spec, `{
volumes: {
additional: [
{
"name": "required",
"claimName": "required-1"
}
],
},
}`)

assert.NilError(t, cc.Create(ctx, custompgadmin))
t.Cleanup(func() { assert.Check(t, cc.Delete(ctx, custompgadmin)) })

err := reconciler.reconcilePGAdminStatefulSet(ctx, custompgadmin, configmap, pvc)
assert.NilError(t, err)

selector, err := naming.AsSelector(metav1.LabelSelector{
MatchLabels: map[string]string{
naming.LabelStandalonePGAdmin: custompgadmin.Name,
},
})
assert.NilError(t, err)

list := appsv1.StatefulSetList{}
assert.NilError(t, cc.List(ctx, &list, client.InNamespace(custompgadmin.Namespace),
client.MatchingLabelsSelector{Selector: selector}))
assert.Equal(t, len(list.Items), 1)

template := list.Items[0].Spec.Template.DeepCopy()

for _, container := range template.Spec.Containers {
assert.Assert(t, cmp.MarshalContains(container.VolumeMounts,
`- mountPath: /etc/pgadmin/conf.d
name: pgadmin-config
readOnly: true
- mountPath: /var/lib/pgadmin
name: pgadmin-data
- mountPath: /etc/pgadmin
name: pgadmin-config-system
readOnly: true
- mountPath: /tmp
name: tmp
- mountPath: /volumes/required
name: volumes-required`))
}

assert.Assert(t, cmp.MarshalContains(template.Spec.Volumes,
`
- name: volumes-required
persistentVolumeClaim:
claimName: required-1`))

// No events created
assert.Equal(t, len(recorder.Events), 0)

})
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ type PGAdminSpec struct {
// https://kubernetes.io/docs/concepts/services-networking/service/
// +optional
ServiceName string `json:"serviceName,omitempty"`

Volumes *PGAdminVolumesSpec `json:"volumes,omitempty"`
}

// PGAdminVolumesSpec defines the configuration for pgAdmin additional volumes
type PGAdminVolumesSpec struct {
// Additional pre-existing volumes to add to the pod.
// ---
// +optional
// +listType=map
// +listMapKey=name
// +kubebuilder:validation:MaxItems=10
Additional []AdditionalVolume `json:"additional,omitempty"`
}

// +kubebuilder:validation:XValidation:rule=`[has(self.postgresClusterName),has(self.postgresClusterSelector)].exists_one(x,x)`,message=`exactly one of "postgresClusterName" or "postgresClusterSelector" is required`
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading