Skip to content

Commit 83faa6e

Browse files
committed
lib/resourcemerge/core: Remove unrecognized volumes and mounts
Since this package was created in d9f6718 (lib: add lib for applying objects, 2018-08-14, openshift#7), the volume(mount) merge logic has required manifest entries to exist, but has allowed in-cluster entries to persist without removal. That hasn't been a problem until [1]: 1. In 4.3, the autoscaler asked for a ca-cert volume mount, based on the cluster-autoscaler-operator-ca config map. 2. In 4.4, the autoscaler dropped those manifest entries [2]. 3. In 4.9, the autoscaler asked the CVO to remove the config map [3]. That lead some born-in 4.3 clusters to have crashlooping autoscalers, because the mount attempts kept failing on the missing config map. We couldn't think of a plausible reason why cluster admins would want to inject additional volume mounts in a CVO-managed pod configuration, so this commit removes that ability and begins clearing away any volume(mount) configuration that is not present in the reconciling manifest. Cluster administrators who do need to add additional mounts in an emergency are free to use ClusterVersion's spec.overrides to take control of a particular CVO-managed resource. This joins a series of similar previous tightenings, including 02bb9ba (lib/resourcemerge/core: Clear env and envFrom if unset in manifest, 2021-04-20, openshift#549) and ca299b8 (lib/resourcemerge: remove ports which are no longer required, 2020-02-13, openshift#322). [1]: https://bugzilla.redhat.com/show_bug.cgi?id=2002834 [2]: openshift/cluster-autoscaler-operator@f08589d#diff-547486373183980619528df695869ed32b80c18383bc16b57a5ee931bf0edd39L89 [3]: openshift/cluster-autoscaler-operator@9a7b3be#diff-d0cf785e044c611986a4d9bdd65bb373c86f9eb1c97bd3f105062184342a872dR4
1 parent c34d95a commit 83faa6e

File tree

2 files changed

+223
-35
lines changed

2 files changed

+223
-35
lines changed

lib/resourcemerge/core.go

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,7 @@ func ensurePodTemplateSpec(modified *bool, existing *corev1.PodTemplateSpec, req
3636
func ensurePodSpec(modified *bool, existing *corev1.PodSpec, required corev1.PodSpec) {
3737
ensureContainers(modified, &existing.InitContainers, required.InitContainers, required.HostNetwork)
3838
ensureContainers(modified, &existing.Containers, required.Containers, required.HostNetwork)
39-
40-
// any volume we specify, we require.
41-
for _, required := range required.Volumes {
42-
var existingCurr *corev1.Volume
43-
for j, curr := range existing.Volumes {
44-
if curr.Name == required.Name {
45-
existingCurr = &existing.Volumes[j]
46-
break
47-
}
48-
}
49-
if existingCurr == nil {
50-
*modified = true
51-
existing.Volumes = append(existing.Volumes, corev1.Volume{})
52-
existingCurr = &existing.Volumes[len(existing.Volumes)-1]
53-
}
54-
ensureVolume(modified, existingCurr, required)
55-
}
39+
ensureVolumes(modified, &existing.Volumes, required.Volumes)
5640

5741
if len(required.RestartPolicy) > 0 {
5842
if existing.RestartPolicy != required.RestartPolicy {
@@ -118,24 +102,7 @@ func ensureContainer(modified *bool, existing *corev1.Container, required corev1
118102
setStringIfSet(modified, &existing.WorkingDir, required.WorkingDir)
119103
ensureResourceRequirements(modified, &existing.Resources, required.Resources)
120104
ensureContainerPorts(modified, &existing.Ports, required.Ports, hostNetwork)
121-
122-
// any volume mount we specify, we require
123-
for _, required := range required.VolumeMounts {
124-
var existingCurr *corev1.VolumeMount
125-
for j, curr := range existing.VolumeMounts {
126-
if curr.Name == required.Name {
127-
existingCurr = &existing.VolumeMounts[j]
128-
break
129-
}
130-
}
131-
if existingCurr == nil {
132-
*modified = true
133-
existing.VolumeMounts = append(existing.VolumeMounts, corev1.VolumeMount{})
134-
existingCurr = &existing.VolumeMounts[len(existing.VolumeMounts)-1]
135-
}
136-
ensureVolumeMount(modified, existingCurr, required)
137-
}
138-
105+
ensureVolumeMounts(modified, &existing.VolumeMounts, required.VolumeMounts)
139106
ensureProbePtr(modified, &existing.LivenessProbe, required.LivenessProbe)
140107
ensureProbePtr(modified, &existing.ReadinessProbe, required.ReadinessProbe)
141108

@@ -347,13 +314,73 @@ func ensureServicePortDefaults(servicePort *corev1.ServicePort) {
347314
}
348315
}
349316

317+
func ensureVolumeMounts(modified *bool, existing *[]corev1.VolumeMount, required []corev1.VolumeMount) {
318+
// any volume mount we specify, we require
319+
exists := struct{}{}
320+
requiredNames := make(map[string]struct{}, len(required))
321+
for _, requiredVolumeMount := range required {
322+
requiredNames[requiredVolumeMount.Name] = exists
323+
var existingCurr *corev1.VolumeMount
324+
for j, curr := range *existing {
325+
if curr.Name == requiredVolumeMount.Name {
326+
existingCurr = &(*existing)[j]
327+
break
328+
}
329+
}
330+
if existingCurr == nil {
331+
*modified = true
332+
*existing = append(*existing, corev1.VolumeMount{})
333+
existingCurr = &(*existing)[len(*existing)-1]
334+
}
335+
ensureVolumeMount(modified, existingCurr, requiredVolumeMount)
336+
}
337+
338+
// any unrecognized volume mount, we remove
339+
for eidx := len(*existing) - 1; eidx >= 0; eidx-- {
340+
if _, ok := requiredNames[(*existing)[eidx].Name]; !ok {
341+
*modified = true
342+
*existing = append((*existing)[:eidx], (*existing)[eidx+1:]...)
343+
}
344+
}
345+
}
346+
350347
func ensureVolumeMount(modified *bool, existing *corev1.VolumeMount, required corev1.VolumeMount) {
351348
if !equality.Semantic.DeepEqual(required, *existing) {
352349
*modified = true
353350
*existing = required
354351
}
355352
}
356353

354+
func ensureVolumes(modified *bool, existing *[]corev1.Volume, required []corev1.Volume) {
355+
// any volume we specify, we require.
356+
exists := struct{}{}
357+
requiredNames := make(map[string]struct{}, len(required))
358+
for _, requiredVolume := range required {
359+
requiredNames[requiredVolume.Name] = exists
360+
var existingCurr *corev1.Volume
361+
for j, curr := range *existing {
362+
if curr.Name == requiredVolume.Name {
363+
existingCurr = &(*existing)[j]
364+
break
365+
}
366+
}
367+
if existingCurr == nil {
368+
*modified = true
369+
*existing = append(*existing, corev1.Volume{})
370+
existingCurr = &(*existing)[len(*existing)-1]
371+
}
372+
ensureVolume(modified, existingCurr, requiredVolume)
373+
}
374+
375+
// any unrecognized volume mount, we remove
376+
for eidx := len(*existing) - 1; eidx >= 0; eidx-- {
377+
if _, ok := requiredNames[(*existing)[eidx].Name]; !ok {
378+
*modified = true
379+
*existing = append((*existing)[:eidx], (*existing)[eidx+1:]...)
380+
}
381+
}
382+
}
383+
357384
func ensureVolume(modified *bool, existing *corev1.Volume, required corev1.Volume) {
358385
if pointer.AllPtrFieldsNil(&required.VolumeSource) {
359386
required.VolumeSource = corev1.VolumeSource{

lib/resourcemerge/core_test.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,167 @@ func TestEnsurePodSpec(t *testing.T) {
451451
},
452452
},
453453
},
454+
{
455+
name: "add volumes on container",
456+
existing: corev1.PodSpec{
457+
Containers: []corev1.Container{
458+
{Name: "test"},
459+
},
460+
},
461+
input: corev1.PodSpec{
462+
Containers: []corev1.Container{
463+
{
464+
Name: "test",
465+
VolumeMounts: []corev1.VolumeMount{
466+
{
467+
Name: "test-volume",
468+
MountPath: "/mnt",
469+
},
470+
},
471+
},
472+
},
473+
Volumes: []corev1.Volume{
474+
{
475+
Name: "test-volume",
476+
VolumeSource: corev1.VolumeSource{
477+
EmptyDir: &corev1.EmptyDirVolumeSource{},
478+
},
479+
},
480+
},
481+
},
482+
expectedModified: true,
483+
expected: corev1.PodSpec{
484+
Containers: []corev1.Container{
485+
{
486+
Name: "test",
487+
VolumeMounts: []corev1.VolumeMount{
488+
{
489+
Name: "test-volume",
490+
MountPath: "/mnt",
491+
},
492+
},
493+
},
494+
},
495+
Volumes: []corev1.Volume{
496+
{
497+
Name: "test-volume",
498+
VolumeSource: corev1.VolumeSource{
499+
EmptyDir: &corev1.EmptyDirVolumeSource{},
500+
},
501+
},
502+
},
503+
},
504+
},
505+
{
506+
name: "modify volumes on container",
507+
existing: corev1.PodSpec{
508+
Containers: []corev1.Container{
509+
{
510+
Name: "test",
511+
VolumeMounts: []corev1.VolumeMount{
512+
{
513+
Name: "test-volume",
514+
MountPath: "/mnt/a",
515+
},
516+
},
517+
},
518+
},
519+
Volumes: []corev1.Volume{
520+
{
521+
Name: "test-volume",
522+
VolumeSource: corev1.VolumeSource{
523+
EmptyDir: &corev1.EmptyDirVolumeSource{},
524+
},
525+
},
526+
},
527+
},
528+
input: corev1.PodSpec{
529+
Containers: []corev1.Container{
530+
{
531+
Name: "test",
532+
VolumeMounts: []corev1.VolumeMount{
533+
{
534+
Name: "test-volume",
535+
MountPath: "/mnt/b",
536+
},
537+
},
538+
},
539+
},
540+
Volumes: []corev1.Volume{
541+
{
542+
Name: "test-volume",
543+
VolumeSource: corev1.VolumeSource{
544+
ConfigMap: &corev1.ConfigMapVolumeSource{
545+
LocalObjectReference: corev1.LocalObjectReference{
546+
Name: "test-config-map",
547+
},
548+
},
549+
},
550+
},
551+
},
552+
},
553+
expectedModified: true,
554+
expected: corev1.PodSpec{
555+
Containers: []corev1.Container{
556+
{
557+
Name: "test",
558+
VolumeMounts: []corev1.VolumeMount{
559+
{
560+
Name: "test-volume",
561+
MountPath: "/mnt/b",
562+
},
563+
},
564+
},
565+
},
566+
Volumes: []corev1.Volume{
567+
{
568+
Name: "test-volume",
569+
VolumeSource: corev1.VolumeSource{
570+
ConfigMap: &corev1.ConfigMapVolumeSource{
571+
LocalObjectReference: corev1.LocalObjectReference{
572+
Name: "test-config-map",
573+
},
574+
},
575+
},
576+
},
577+
},
578+
},
579+
},
580+
{
581+
name: "remove container volumes",
582+
existing: corev1.PodSpec{
583+
Containers: []corev1.Container{
584+
{
585+
Name: "test",
586+
VolumeMounts: []corev1.VolumeMount{
587+
{
588+
Name: "test-volume",
589+
MountPath: "/mnt",
590+
},
591+
},
592+
},
593+
},
594+
Volumes: []corev1.Volume{
595+
{
596+
Name: "test-volume",
597+
VolumeSource: corev1.VolumeSource{
598+
EmptyDir: &corev1.EmptyDirVolumeSource{},
599+
},
600+
},
601+
},
602+
},
603+
input: corev1.PodSpec{
604+
Containers: []corev1.Container{
605+
{Name: "test"},
606+
},
607+
},
608+
expectedModified: true,
609+
expected: corev1.PodSpec{
610+
Containers: []corev1.Container{
611+
{Name: "test"},
612+
},
613+
},
614+
},
454615
}
455616

456617
for _, test := range tests {

0 commit comments

Comments
 (0)