Skip to content

Commit bc30b30

Browse files
author
Nikolas De Giorgis
authored
CLOUDP-73217: fix volumes merging (#204)
1 parent a1b7305 commit bc30b30

File tree

2 files changed

+182
-14
lines changed

2 files changed

+182
-14
lines changed

pkg/kube/podtemplatespec/podspec_template.go

Lines changed: 125 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -265,19 +265,136 @@ func MergePodTemplateSpecs(defaultTemplate, overrideTemplate corev1.PodTemplateS
265265
return mergedPodTemplateSpec, nil
266266
}
267267

268+
func createKeyToPathMap(items []corev1.KeyToPath) map[string]corev1.KeyToPath {
269+
itemsMap := make(map[string]corev1.KeyToPath)
270+
for _, v := range items {
271+
itemsMap[v.Key] = v
272+
}
273+
return itemsMap
274+
}
275+
276+
func mergeKeyToPath(defaultKey corev1.KeyToPath, overrideKey corev1.KeyToPath) corev1.KeyToPath {
277+
if defaultKey.Key != overrideKey.Key {
278+
// This should not happen as we always merge by Key.
279+
// If it does, we return the default as something's wrong
280+
return defaultKey
281+
}
282+
if overrideKey.Path != "" {
283+
defaultKey.Path = overrideKey.Path
284+
}
285+
if overrideKey.Mode != nil {
286+
defaultKey.Mode = overrideKey.Mode
287+
}
288+
return defaultKey
289+
}
290+
291+
func mergeKeyToPathItems(defaultItems []corev1.KeyToPath, overrideItems []corev1.KeyToPath) []corev1.KeyToPath {
292+
// Merge Items array by KeyToPath.Key entry
293+
defaultItemsMap := createKeyToPathMap(defaultItems)
294+
overrideItemsMap := createKeyToPathMap(overrideItems)
295+
var mergedItems []corev1.KeyToPath
296+
for _, defaultItem := range defaultItemsMap {
297+
mergedKey := defaultItem
298+
if overrideItem, ok := overrideItemsMap[defaultItem.Key]; ok {
299+
// Need to merge
300+
mergedKey = mergeKeyToPath(defaultItem, overrideItem)
301+
}
302+
mergedItems = append(mergedItems, mergedKey)
303+
}
304+
for _, overrideItem := range overrideItemsMap {
305+
if _, ok := defaultItemsMap[overrideItem.Key]; ok {
306+
// Already merged
307+
continue
308+
}
309+
mergedItems = append(mergedItems, overrideItem)
310+
311+
}
312+
return mergedItems
313+
}
314+
315+
func mergeVolume(defaultVolume corev1.Volume, overrideVolume corev1.Volume) corev1.Volume {
316+
// Volume contains only Name and VolumeSource
317+
318+
// Merge VolumeSource
319+
overrideSource := overrideVolume.VolumeSource
320+
defaultSource := defaultVolume.VolumeSource
321+
mergedVolume := defaultVolume
322+
323+
// Only one field must be non-nil.
324+
// We merge if it is one of the ones we fill from the operator side:
325+
// - EmptyDir
326+
if overrideSource.EmptyDir != nil && defaultSource.EmptyDir != nil {
327+
if overrideSource.EmptyDir.Medium != "" {
328+
mergedVolume.EmptyDir.Medium = overrideSource.EmptyDir.Medium
329+
}
330+
if overrideSource.EmptyDir.SizeLimit != nil {
331+
mergedVolume.EmptyDir.SizeLimit = overrideSource.EmptyDir.SizeLimit
332+
}
333+
return mergedVolume
334+
}
335+
// - Secret
336+
if overrideSource.Secret != nil && defaultSource.Secret != nil {
337+
if overrideSource.Secret.SecretName != "" {
338+
mergedVolume.Secret.SecretName = overrideSource.Secret.SecretName
339+
}
340+
mergedVolume.Secret.Items = mergeKeyToPathItems(defaultSource.Secret.Items, overrideSource.Secret.Items)
341+
if overrideSource.Secret.DefaultMode != nil {
342+
mergedVolume.Secret.DefaultMode = overrideSource.Secret.DefaultMode
343+
}
344+
return mergedVolume
345+
}
346+
// - ConfigMap
347+
if overrideSource.ConfigMap != nil && defaultSource.ConfigMap != nil {
348+
if overrideSource.ConfigMap.LocalObjectReference.Name != "" {
349+
mergedVolume.ConfigMap.LocalObjectReference.Name = overrideSource.ConfigMap.LocalObjectReference.Name
350+
}
351+
mergedVolume.ConfigMap.Items = mergeKeyToPathItems(defaultSource.ConfigMap.Items, overrideSource.ConfigMap.Items)
352+
if overrideSource.ConfigMap.DefaultMode != nil {
353+
mergedVolume.ConfigMap.DefaultMode = overrideSource.ConfigMap.DefaultMode
354+
}
355+
if overrideSource.ConfigMap.Optional != nil {
356+
mergedVolume.ConfigMap.Optional = overrideSource.ConfigMap.Optional
357+
}
358+
return mergedVolume
359+
}
360+
361+
// Otherwise we assume that the user provides every field
362+
// and we just assign it and nil every other field
363+
// We also do that in the case the user provides one of the three listed above
364+
// but our volume has a different non-nil entry.
365+
366+
// this is effectively the same as just returning the overrideSource
367+
mergedVolume.VolumeSource = overrideSource
368+
return mergedVolume
369+
}
370+
371+
func createVolumesMap(volumes []corev1.Volume) map[string]corev1.Volume {
372+
volumesMap := make(map[string]corev1.Volume)
373+
for _, v := range volumes {
374+
volumesMap[v.Name] = v
375+
}
376+
return volumesMap
377+
}
378+
268379
func mergeVolumes(defaultVolumes []corev1.Volume, overrideVolumes []corev1.Volume) []corev1.Volume {
269-
mergedVolumeMap := make(map[string]corev1.Volume)
380+
defaultVolumesMap := createVolumesMap(defaultVolumes)
381+
overrideVolumesMap := createVolumesMap(overrideVolumes)
270382
mergedVolumes := []corev1.Volume{}
271-
for _, v := range defaultVolumes {
272-
mergedVolumeMap[v.Name] = v
273-
}
274383

275-
for _, v := range overrideVolumes {
276-
mergedVolumeMap[v.Name] = v
384+
for _, defaultVolume := range defaultVolumes {
385+
mergedVolume := defaultVolume
386+
if overrideVolume, ok := overrideVolumesMap[defaultVolume.Name]; ok {
387+
mergedVolume = mergeVolume(defaultVolume, overrideVolume)
388+
}
389+
mergedVolumes = append(mergedVolumes, mergedVolume)
277390
}
278391

279-
for _, v := range mergedVolumeMap {
280-
mergedVolumes = append(mergedVolumes, v)
392+
for _, overrideVolume := range overrideVolumes {
393+
if _, ok := defaultVolumesMap[overrideVolume.Name]; ok {
394+
// Already Merged
395+
continue
396+
}
397+
mergedVolumes = append(mergedVolumes, overrideVolume)
281398
}
282399

283400
sort.SliceStable(mergedVolumes, func(i, j int) bool {

pkg/kube/podtemplatespec/podspec_template_test.go

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,26 +304,26 @@ func TestMergeVolumes_DoesNotAddDuplicatesWithSameName(t *testing.T) {
304304
})
305305

306306
overridePodSpec := getDefaultPodSpec()
307-
defaultPodSpec.Spec.Volumes = append(defaultPodSpec.Spec.Volumes, corev1.Volume{
307+
overridePodSpec.Spec.Volumes = append(overridePodSpec.Spec.Volumes, corev1.Volume{
308308
Name: "new-volume",
309309
VolumeSource: corev1.VolumeSource{
310310
HostPath: &corev1.HostPathVolumeSource{
311311
Path: "updated-host-path",
312312
},
313313
},
314314
})
315-
defaultPodSpec.Spec.Volumes = append(defaultPodSpec.Spec.Volumes, corev1.Volume{
315+
overridePodSpec.Spec.Volumes = append(overridePodSpec.Spec.Volumes, corev1.Volume{
316316
Name: "new-volume-3",
317317
})
318318

319319
mergedPodSpecTemplate, err := MergePodTemplateSpecs(defaultPodSpec, overridePodSpec)
320320
assert.NoError(t, err)
321321

322322
assert.Len(t, mergedPodSpecTemplate.Spec.Volumes, 3)
323-
assert.Equal(t, mergedPodSpecTemplate.Spec.Volumes[0].Name, "new-volume")
324-
assert.Equal(t, mergedPodSpecTemplate.Spec.Volumes[0].VolumeSource.HostPath.Path, "updated-host-path")
325-
assert.Equal(t, mergedPodSpecTemplate.Spec.Volumes[1].Name, "new-volume-2")
326-
assert.Equal(t, mergedPodSpecTemplate.Spec.Volumes[2].Name, "new-volume-3")
323+
assert.Equal(t, "new-volume", mergedPodSpecTemplate.Spec.Volumes[0].Name)
324+
assert.Equal(t, "updated-host-path", mergedPodSpecTemplate.Spec.Volumes[0].VolumeSource.HostPath.Path)
325+
assert.Equal(t, "new-volume-2", mergedPodSpecTemplate.Spec.Volumes[1].Name)
326+
assert.Equal(t, "new-volume-3", mergedPodSpecTemplate.Spec.Volumes[2].Name)
327327
}
328328

329329
func TestMergeVolumeMounts(t *testing.T) {
@@ -342,6 +342,57 @@ func TestMergeVolumeMounts(t *testing.T) {
342342
assert.Equal(t, []corev1.VolumeMount{vol2, vol0}, mergedVolumeMounts)
343343
}
344344

345+
func TestMergeVolumesSecret(t *testing.T) {
346+
permission := int32(416)
347+
vol0 := []corev1.Volume{{Name: "volume", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: "Secret-name"}}}}
348+
vol1 := []corev1.Volume{{Name: "volume", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{DefaultMode: &permission}}}}
349+
mergedVolumes := mergeVolumes(vol0, vol1)
350+
assert.Len(t, mergedVolumes, 1)
351+
volume := mergedVolumes[0]
352+
assert.Equal(t, "volume", volume.Name)
353+
assert.Equal(t, corev1.SecretVolumeSource{SecretName: "Secret-name", DefaultMode: &permission}, *volume.Secret)
354+
}
355+
356+
func TestMergeNonNilValueNotFilledByOperator(t *testing.T) {
357+
// Tests that providing a custom volume with a volume source
358+
// That the operator does not manage overwrites the original
359+
vol0 := []corev1.Volume{{Name: "volume", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: "Secret-name"}}}}
360+
vol1 := []corev1.Volume{{Name: "volume", VolumeSource: corev1.VolumeSource{GCEPersistentDisk: &corev1.GCEPersistentDiskVolumeSource{}}}}
361+
mergedVolumes := mergeVolumes(vol0, vol1)
362+
assert.Len(t, mergedVolumes, 1)
363+
volume := mergedVolumes[0]
364+
assert.Equal(t, "volume", volume.Name)
365+
assert.Equal(t, corev1.GCEPersistentDiskVolumeSource{}, *volume.GCEPersistentDisk)
366+
assert.Nil(t, volume.Secret)
367+
}
368+
369+
func TestMergeNonNilValueFilledByOperatorButDifferent(t *testing.T) {
370+
// Tests that providing a custom volume with a volume source
371+
// That the operator does manage, but different from the one
372+
// That already exists, overwrites the original
373+
vol0 := []corev1.Volume{{Name: "volume", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: "Secret-name"}}}}
374+
vol1 := []corev1.Volume{{Name: "volume", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}}
375+
mergedVolumes := mergeVolumes(vol0, vol1)
376+
assert.Len(t, mergedVolumes, 1)
377+
volume := mergedVolumes[0]
378+
assert.Equal(t, "volume", volume.Name)
379+
assert.Equal(t, corev1.EmptyDirVolumeSource{}, *volume.EmptyDir)
380+
assert.Nil(t, volume.Secret)
381+
}
382+
383+
func TestMergeVolumeAddVolume(t *testing.T) {
384+
vol0 := []corev1.Volume{{Name: "volume0", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{}}}}
385+
vol1 := []corev1.Volume{{Name: "volume1", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}}
386+
mergedVolumes := mergeVolumes(vol0, vol1)
387+
assert.Len(t, mergedVolumes, 2)
388+
volume0 := mergedVolumes[0]
389+
assert.Equal(t, "volume0", volume0.Name)
390+
assert.Equal(t, corev1.SecretVolumeSource{}, *volume0.Secret)
391+
volume1 := mergedVolumes[1]
392+
assert.Equal(t, "volume1", volume1.Name)
393+
assert.Equal(t, corev1.EmptyDirVolumeSource{}, *volume1.EmptyDir)
394+
}
395+
345396
func int64Ref(i int64) *int64 {
346397
return &i
347398
}

0 commit comments

Comments
 (0)