Skip to content

Commit b29af36

Browse files
authored
Refactor Merging Strategy #6 (#292)
1 parent 49184ac commit b29af36

File tree

6 files changed

+433
-39
lines changed

6 files changed

+433
-39
lines changed

pkg/kube/podtemplatespec/podspec_template.go

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package podtemplatespec
33
import (
44
"github.com/mongodb/mongodb-kubernetes-operator/pkg/util/merge"
55

6-
"github.com/imdario/mergo"
76
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/container"
87
corev1 "k8s.io/api/core/v1"
98
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -228,43 +227,8 @@ func WithVolumeMounts(containerName string, volumeMounts ...corev1.VolumeMount)
228227
}
229228

230229
func MergePodTemplateSpecs(defaultTemplate, overrideTemplate corev1.PodTemplateSpec) (corev1.PodTemplateSpec, error) {
231-
232-
mergedContainers := merge.Containers(defaultTemplate.Spec.Containers, overrideTemplate.Spec.Containers)
233-
mergedTolerations := merge.Tolerations(defaultTemplate.Spec.Tolerations, overrideTemplate.Spec.Tolerations)
234-
mergedInitContainers := merge.Containers(defaultTemplate.Spec.InitContainers, overrideTemplate.Spec.InitContainers)
235-
mergedAffinity, err := mergeAffinity(defaultTemplate.Spec.Affinity, overrideTemplate.Spec.Affinity)
236-
if err != nil {
237-
return corev1.PodTemplateSpec{}, err
238-
}
239-
240-
mergedVolumes := merge.Volumes(defaultTemplate.Spec.Volumes, overrideTemplate.Spec.Volumes)
241-
242-
// Everything else can be merged with mergo
243-
mergedPodTemplateSpec := *defaultTemplate.DeepCopy()
244-
if err = mergo.Merge(&mergedPodTemplateSpec, overrideTemplate, mergo.WithOverride, mergo.WithAppendSlice); err != nil {
245-
return corev1.PodTemplateSpec{}, err
246-
}
247-
248-
mergedPodTemplateSpec.Spec.Containers = mergedContainers
249-
mergedPodTemplateSpec.Spec.Tolerations = mergedTolerations
250-
mergedPodTemplateSpec.Spec.InitContainers = mergedInitContainers
251-
mergedPodTemplateSpec.Spec.Affinity = mergedAffinity
252-
mergedPodTemplateSpec.Spec.Volumes = mergedVolumes
253-
return mergedPodTemplateSpec, nil
254-
}
255-
256-
func mergeAffinity(defaultAffinity, overrideAffinity *corev1.Affinity) (*corev1.Affinity, error) {
257-
if defaultAffinity == nil {
258-
return overrideAffinity, nil
259-
}
260-
if overrideAffinity == nil {
261-
return defaultAffinity, nil
262-
}
263-
mergedAffinity := defaultAffinity.DeepCopy()
264-
if err := mergo.Merge(mergedAffinity, *overrideAffinity, mergo.WithOverride); err != nil {
265-
return nil, err
266-
}
267-
return mergedAffinity, nil
230+
// TODO: remove in place of direct call to merge.PodTemplateSpecs
231+
return merge.PodTemplateSpecs(defaultTemplate, overrideTemplate), nil
268232
}
269233

270234
// findContainerByName will find either a container or init container by name in a pod template spec

pkg/kube/podtemplatespec/podspec_template_test.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,23 @@ func TestMerge(t *testing.T) {
155155
Affinity: affinity("zone", "custom"),
156156
},
157157
}
158-
assert.Equal(t, expected, mergedSpec)
158+
assert.Equal(t, expected.Name, mergedSpec.Name)
159+
assert.Equal(t, expected.Namespace, mergedSpec.Namespace)
160+
assert.Equal(t, expected.Labels["app"], mergedSpec.Labels["app"])
161+
assert.Equal(t, expected.Labels["custom"], mergedSpec.Labels["custom"])
162+
assert.Equal(t, expected.Spec.NodeSelector["node-0"], mergedSpec.Spec.NodeSelector["node-0"])
163+
assert.Equal(t, expected.Spec.NodeSelector["node-1"], mergedSpec.Spec.NodeSelector["node-1"])
164+
assert.Equal(t, expected.Spec.ServiceAccountName, mergedSpec.Spec.ServiceAccountName)
165+
assert.Equal(t, expected.Spec.TerminationGracePeriodSeconds, mergedSpec.Spec.TerminationGracePeriodSeconds)
166+
assert.Equal(t, expected.Spec.ActiveDeadlineSeconds, mergedSpec.Spec.ActiveDeadlineSeconds)
167+
assert.Equal(t, expected.Spec.NodeName, mergedSpec.Spec.NodeName)
168+
assert.Equal(t, expected.Spec.RestartPolicy, mergedSpec.Spec.RestartPolicy)
169+
assert.Equal(t, expected.Spec.Volumes, mergedSpec.Spec.Volumes)
170+
assert.Equal(t, expected.Spec.Affinity.PodAntiAffinity, mergedSpec.Spec.Affinity.PodAntiAffinity)
171+
assert.Equal(t, expected.Spec.Affinity.PodAffinity, mergedSpec.Spec.Affinity.PodAffinity)
172+
assert.Equal(t, expected.Spec.Affinity.NodeAffinity, mergedSpec.Spec.Affinity.NodeAffinity)
173+
assert.Equal(t, expected.Spec.Containers, mergedSpec.Spec.Containers)
174+
assert.Equal(t, expected.Spec.InitContainers, mergedSpec.Spec.InitContainers)
159175
}
160176

161177
func TestMergeFromEmpty(t *testing.T) {

pkg/util/merge/merge.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ func StringSlices(slice1, slice2 []string) []string {
2020
return mergedStrings
2121
}
2222

23+
// StringToStringMap merges two string maps together with the second map
24+
// overriding any values also specified in the first.
25+
func StringToStringMap(map1, map2 map[string]string) map[string]string {
26+
mergedMap := make(map[string]string)
27+
for k, v := range map1 {
28+
mergedMap[k] = v
29+
}
30+
for k, v := range map2 {
31+
mergedMap[k] = v
32+
}
33+
return mergedMap
34+
}
35+
2336
// Containers merges two slices of containers merging each item by container name.
2437
func Containers(defaultContainers, overrideContainers []corev1.Container) []corev1.Container {
2538
mergedContainerMap := map[string]corev1.Container{}
@@ -629,3 +642,24 @@ func createKeyToPathMap(items []corev1.KeyToPath) map[string]corev1.KeyToPath {
629642
}
630643
return itemsMap
631644
}
645+
646+
// Affinity merges two corev1.Affinity types.
647+
func Affinity(defaultAffinity, overrideAffinity *corev1.Affinity) *corev1.Affinity {
648+
if defaultAffinity == nil {
649+
return overrideAffinity
650+
}
651+
if overrideAffinity == nil {
652+
return defaultAffinity
653+
}
654+
mergedAffinity := defaultAffinity.DeepCopy()
655+
if overrideAffinity.NodeAffinity != nil {
656+
mergedAffinity.NodeAffinity = overrideAffinity.NodeAffinity
657+
}
658+
if overrideAffinity.PodAffinity != nil {
659+
mergedAffinity.PodAffinity = overrideAffinity.PodAffinity
660+
}
661+
if overrideAffinity.PodAntiAffinity != nil {
662+
mergedAffinity.PodAntiAffinity = overrideAffinity.PodAntiAffinity
663+
}
664+
return mergedAffinity
665+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package merge
2+
3+
import (
4+
"sort"
5+
6+
corev1 "k8s.io/api/core/v1"
7+
)
8+
9+
// EphemeralContainers merges two slices of EphemeralContainers merging each item by container name.
10+
func EphemeralContainers(defaultContainers, overrideContainers []corev1.EphemeralContainer) []corev1.EphemeralContainer {
11+
mergedContainerMap := map[string]corev1.EphemeralContainer{}
12+
13+
originalMap := createEphemeralContainerMap(defaultContainers)
14+
overrideMap := createEphemeralContainerMap(overrideContainers)
15+
16+
for k, v := range originalMap {
17+
mergedContainerMap[k] = v
18+
}
19+
20+
for k, v := range overrideMap {
21+
if orig, ok := originalMap[k]; ok {
22+
mergedContainerMap[k] = EphemeralContainer(orig, v)
23+
} else {
24+
mergedContainerMap[k] = v
25+
}
26+
}
27+
28+
var mergedContainers []corev1.EphemeralContainer
29+
for _, v := range mergedContainerMap {
30+
mergedContainers = append(mergedContainers, v)
31+
}
32+
33+
sort.SliceStable(mergedContainers, func(i, j int) bool {
34+
return mergedContainers[i].Name < mergedContainers[j].Name
35+
})
36+
return mergedContainers
37+
38+
}
39+
40+
// EphemeralContainer merges two EphemeralContainers together.
41+
func EphemeralContainer(defaultContainer, overrideContainer corev1.EphemeralContainer) corev1.EphemeralContainer {
42+
merged := defaultContainer
43+
44+
if overrideContainer.Name != "" {
45+
merged.Name = overrideContainer.Name
46+
}
47+
48+
if overrideContainer.Image != "" {
49+
merged.Image = overrideContainer.Image
50+
}
51+
52+
merged.Command = StringSlices(defaultContainer.Command, overrideContainer.Command)
53+
merged.Args = StringSlices(defaultContainer.Args, overrideContainer.Args)
54+
55+
if overrideContainer.WorkingDir != "" {
56+
merged.WorkingDir = overrideContainer.WorkingDir
57+
}
58+
59+
merged.Ports = ContainerPortSlicesByName(defaultContainer.Ports, overrideContainer.Ports)
60+
merged.Env = Envs(defaultContainer.Env, overrideContainer.Env)
61+
merged.Resources = ResourceRequirements(defaultContainer.Resources, overrideContainer.Resources)
62+
merged.VolumeMounts = VolumeMounts(defaultContainer.VolumeMounts, overrideContainer.VolumeMounts)
63+
merged.VolumeDevices = VolumeDevices(defaultContainer.VolumeDevices, overrideContainer.VolumeDevices)
64+
merged.LivenessProbe = Probe(defaultContainer.LivenessProbe, overrideContainer.LivenessProbe)
65+
merged.ReadinessProbe = Probe(defaultContainer.ReadinessProbe, overrideContainer.ReadinessProbe)
66+
merged.StartupProbe = Probe(defaultContainer.StartupProbe, overrideContainer.StartupProbe)
67+
merged.Lifecycle = LifeCycle(defaultContainer.Lifecycle, overrideContainer.Lifecycle)
68+
69+
if overrideContainer.TerminationMessagePath != "" {
70+
merged.TerminationMessagePath = overrideContainer.TerminationMessagePath
71+
}
72+
73+
if overrideContainer.TerminationMessagePolicy != "" {
74+
merged.TerminationMessagePolicy = overrideContainer.TerminationMessagePolicy
75+
}
76+
77+
if overrideContainer.ImagePullPolicy != "" {
78+
merged.ImagePullPolicy = overrideContainer.ImagePullPolicy
79+
}
80+
81+
merged.SecurityContext = SecurityContext(defaultContainer.SecurityContext, overrideContainer.SecurityContext)
82+
83+
if overrideContainer.Stdin {
84+
merged.Stdin = overrideContainer.Stdin
85+
}
86+
87+
if overrideContainer.StdinOnce {
88+
merged.StdinOnce = overrideContainer.StdinOnce
89+
}
90+
91+
if overrideContainer.TTY {
92+
merged.TTY = overrideContainer.TTY
93+
}
94+
95+
// EphemeralContainer only fields
96+
if overrideContainer.TargetContainerName != "" {
97+
merged.TargetContainerName = overrideContainer.TargetContainerName
98+
}
99+
100+
return merged
101+
}
102+
103+
func createEphemeralContainerMap(containers []corev1.EphemeralContainer) map[string]corev1.EphemeralContainer {
104+
m := make(map[string]corev1.EphemeralContainer)
105+
for _, v := range containers {
106+
m[v.Name] = v
107+
}
108+
return m
109+
}

0 commit comments

Comments
 (0)