Skip to content

Attach additional volume for postgres #4210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -11027,6 +11027,50 @@ spec:
type: array
volumes:
properties:
additional:
description: Additional pre-existing volumes to add to the
pod.
items:
properties:
claimName:
description: A reference to a preexisting PVC.
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 containers to attach this volume to.
An omitted `Containers` field matches all containers.
An empty `Containers` field matches no containers.
items:
type: string
maxItems: 10
type: array
x-kubernetes-list-type: atomic
name:
allOf:
- maxLength: 63
- maxLength: 55
description: |-
The name of the volume used for mounting path.
Volumes are mounted in the pods at `volumes/<NAME>`
Must be unique.
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
readOnly:
description: Sets the write/read mode of the volume
type: boolean
required:
- claimName
- name
type: object
maxItems: 10
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
temp:
description: |-
An ephemeral volume for temporary files.
Expand Down Expand Up @@ -29598,6 +29642,50 @@ spec:
type: array
volumes:
properties:
additional:
description: Additional pre-existing volumes to add to the
pod.
items:
properties:
claimName:
description: A reference to a preexisting PVC.
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 containers to attach this volume to.
An omitted `Containers` field matches all containers.
An empty `Containers` field matches no containers.
items:
type: string
maxItems: 10
type: array
x-kubernetes-list-type: atomic
name:
allOf:
- maxLength: 63
- maxLength: 55
description: |-
The name of the volume used for mounting path.
Volumes are mounted in the pods at `volumes/<NAME>`
Must be unique.
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
readOnly:
description: Sets the write/read mode of the volume
type: boolean
required:
- claimName
- name
type: object
maxItems: 10
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
temp:
description: |-
An ephemeral volume for temporary files.
Expand Down
10 changes: 10 additions & 0 deletions internal/controller/postgrescluster/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,16 @@ func (r *Reconciler) reconcileInstance(
addDevSHM(&instance.Spec.Template)
}

// 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)

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

if err == nil {
err = errors.WithStack(r.apply(ctx, instance))
}
Expand Down
80 changes: 80 additions & 0 deletions internal/controller/postgrescluster/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/rand"
"k8s.io/apimachinery/pkg/util/sets"

"github.com/crunchydata/postgres-operator/internal/initialize"
"github.com/crunchydata/postgres-operator/internal/naming"
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
)

var tmpDirSizeLimit = resource.MustParse("16Mi")
Expand Down Expand Up @@ -285,3 +287,81 @@ func safeHash32(content func(w io.Writer) error) (string, error) {
}
return rand.SafeEncodeString(fmt.Sprint(hash.Sum32())), nil
}

// AdditionalVolumeMount returns the name and mount path of the additional volume.
func AdditionalVolumeMount(name string, readOnly bool) corev1.VolumeMount {
return corev1.VolumeMount{
Name: fmt.Sprintf("volumes-%s", name),
MountPath: "/volumes/" + name,
ReadOnly: readOnly,
}
}

// addAdditionalVolumesToSpecifiedContainers adds additional volumes to the specified
// containers in the specified 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,
additionalVolumes []v1beta1.AdditionalVolume) []string {

missingContainers := []string{}
for _, additionalVolumeRequest := range additionalVolumes {

additionalVolumeMount := AdditionalVolumeMount(
additionalVolumeRequest.Name,
additionalVolumeRequest.ReadOnly,
)

additionalVolume := corev1.Volume{
Name: additionalVolumeMount.Name,
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: additionalVolumeRequest.ClaimName,
ReadOnly: additionalVolumeMount.ReadOnly,
},
},
}

// Create a set of all the requested containers,
// then in the loops below when we attach the volume to a container,
// we can safely remove that container name from the set.
// This gives us a way to track the containers that are requested but not found.
// This relies on `containers` and `initContainers` together being unique.
// - https://github.com/kubernetes/api/blob/b40c1cacbb902b21a7e0c7bf0967321860c1a632/core/v1/types.go#L3895C27-L3896C33
names := sets.New(additionalVolumeRequest.Containers...)
allContainers := false
// If the containers list is omitted, we add the volume to all containers
if additionalVolumeRequest.Containers == nil {
allContainers = true
}

for i := range template.Spec.Containers {
if allContainers || names.Has(template.Spec.Containers[i].Name) {
template.Spec.Containers[i].VolumeMounts = append(
template.Spec.Containers[i].VolumeMounts,
additionalVolumeMount)

names.Delete(template.Spec.Containers[i].Name)
}
}

for i := range template.Spec.InitContainers {
if allContainers || names.Has(template.Spec.InitContainers[i].Name) {
template.Spec.InitContainers[i].VolumeMounts = append(
template.Spec.InitContainers[i].VolumeMounts,
additionalVolumeMount)

names.Delete(template.Spec.InitContainers[i].Name)

}
}

missingContainers = append(missingContainers, names.UnsortedList()...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 Using Delete to subtract from names relies on init and regular containers having unique names together. That's correct and fine, but I wonder if it is worth calling out.

🤔 Should the resulting slice be a Set?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was about to raise this concern myself... Is it correct though, when you take sidecars into consideration? Couldn't the user add a sidecar to Containers that has the same name as one in InitContainers?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answering my own question: No, the user could not add a sidecar with the same name as an init container. Ben tried it out and the Pod fails to roll out. He also found this in k8s documentation: “The name of each app and init container in a Pod must be unique; a validation error is thrown for any container sharing a name with another.”


template.Spec.Volumes = append(
template.Spec.Volumes,
additionalVolume)
}
return missingContainers
}
Loading
Loading