Skip to content
This repository was archived by the owner on Dec 12, 2025. It is now read-only.

Commit 74d3d7a

Browse files
authored
CLOUDP-80014: Refactor Merging - Probes, Security Context & LifeCycle (#288)
1 parent ceafb81 commit 74d3d7a

File tree

2 files changed

+203
-11
lines changed

2 files changed

+203
-11
lines changed

pkg/util/merge/merge.go

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,10 @@ func Container(defaultContainer, overrideContainer corev1.Container) corev1.Cont
4747
merged.Resources = ResourceRequirements(defaultContainer.Resources, overrideContainer.Resources)
4848
merged.VolumeMounts = VolumeMounts(defaultContainer.VolumeMounts, overrideContainer.VolumeMounts)
4949
merged.VolumeDevices = VolumeDevices(defaultContainer.VolumeDevices, overrideContainer.VolumeDevices)
50-
51-
// TODO: merge LivenessProbe
52-
53-
// TODO: merge ReadinessProve
54-
55-
// TODO: merge StartupProbe
56-
57-
// TODO: merge Lifecycle
50+
merged.LivenessProbe = Probe(defaultContainer.LivenessProbe, overrideContainer.LivenessProbe)
51+
merged.ReadinessProbe = Probe(defaultContainer.ReadinessProbe, overrideContainer.ReadinessProbe)
52+
merged.StartupProbe = Probe(defaultContainer.StartupProbe, overrideContainer.StartupProbe)
53+
merged.Lifecycle = LifeCycle(defaultContainer.Lifecycle, overrideContainer.Lifecycle)
5854

5955
if overrideContainer.TerminationMessagePath != "" {
6056
merged.TerminationMessagePath = overrideContainer.TerminationMessagePath
@@ -68,7 +64,7 @@ func Container(defaultContainer, overrideContainer corev1.Container) corev1.Cont
6864
merged.ImagePullPolicy = overrideContainer.ImagePullPolicy
6965
}
7066

71-
// TODO: merge SecurityContext
67+
merged.SecurityContext = SecurityContext(defaultContainer.SecurityContext, overrideContainer.SecurityContext)
7268

7369
if overrideContainer.Stdin {
7470
merged.Stdin = overrideContainer.Stdin
@@ -85,6 +81,110 @@ func Container(defaultContainer, overrideContainer corev1.Container) corev1.Cont
8581
return merged
8682
}
8783

84+
// Probe merges the contents of two probes together.
85+
func Probe(original, override *corev1.Probe) *corev1.Probe {
86+
if override == nil {
87+
return original
88+
}
89+
if original == nil {
90+
return override
91+
}
92+
merged := *original
93+
if override.Handler.Exec != nil {
94+
merged.Handler.Exec = override.Handler.Exec
95+
}
96+
if override.Handler.HTTPGet != nil {
97+
merged.Handler.HTTPGet = override.Handler.HTTPGet
98+
}
99+
if override.Handler.TCPSocket != nil {
100+
merged.Handler.TCPSocket = override.Handler.TCPSocket
101+
}
102+
if override.InitialDelaySeconds != 0 {
103+
merged.InitialDelaySeconds = override.InitialDelaySeconds
104+
}
105+
if override.TimeoutSeconds != 0 {
106+
merged.TimeoutSeconds = override.TimeoutSeconds
107+
}
108+
if override.PeriodSeconds != 0 {
109+
merged.PeriodSeconds = override.PeriodSeconds
110+
}
111+
112+
if override.SuccessThreshold != 0 {
113+
merged.SuccessThreshold = override.SuccessThreshold
114+
}
115+
116+
if override.FailureThreshold != 0 {
117+
merged.FailureThreshold = override.FailureThreshold
118+
}
119+
return &merged
120+
}
121+
122+
// LifeCycle merges two LifeCycles.
123+
func LifeCycle(original, override *corev1.Lifecycle) *corev1.Lifecycle {
124+
if override == nil {
125+
return original
126+
}
127+
if original == nil {
128+
return override
129+
}
130+
merged := *original
131+
132+
if override.PostStart != nil {
133+
merged.PostStart = override.PostStart
134+
}
135+
if override.PreStop != nil {
136+
merged.PreStop = override.PreStop
137+
}
138+
return &merged
139+
}
140+
141+
// SecurityContext merges two security contexts.
142+
func SecurityContext(original, override *corev1.SecurityContext) *corev1.SecurityContext {
143+
if override == nil {
144+
return original
145+
}
146+
if original == nil {
147+
return override
148+
}
149+
merged := *original
150+
151+
if override.Capabilities != nil {
152+
merged.Capabilities = override.Capabilities
153+
}
154+
155+
if override.Privileged != nil {
156+
merged.Privileged = override.Privileged
157+
}
158+
159+
if override.SELinuxOptions != nil {
160+
merged.SELinuxOptions = override.SELinuxOptions
161+
}
162+
163+
if override.WindowsOptions != nil {
164+
merged.WindowsOptions = override.WindowsOptions
165+
}
166+
if override.RunAsUser != nil {
167+
merged.RunAsUser = override.RunAsUser
168+
}
169+
if override.RunAsGroup != nil {
170+
merged.RunAsGroup = override.RunAsGroup
171+
}
172+
if override.RunAsNonRoot != nil {
173+
merged.RunAsNonRoot = override.RunAsNonRoot
174+
}
175+
if override.ReadOnlyRootFilesystem != nil {
176+
merged.ReadOnlyRootFilesystem = override.ReadOnlyRootFilesystem
177+
}
178+
if override.AllowPrivilegeEscalation != nil {
179+
merged.AllowPrivilegeEscalation = override.AllowPrivilegeEscalation
180+
}
181+
if override.ProcMount != nil {
182+
merged.ProcMount = override.ProcMount
183+
}
184+
return &merged
185+
}
186+
187+
// VolumeDevices merges two slices of VolumeDevices by name.
88188
func VolumeDevices(original, override []corev1.VolumeDevice) []corev1.VolumeDevice {
89189
mergedDevicesMap := map[string]corev1.VolumeDevice{}
90190
originalDevicesMap := createVolumeDevicesMap(original)
@@ -258,6 +358,7 @@ func createContainerPortMap(containerPorts []corev1.ContainerPort) map[string]co
258358
return containerPortMap
259359
}
260360

361+
// VolumeMounts merges two slices of volume mounts by name.
261362
func VolumeMounts(original, override []corev1.VolumeMount) []corev1.VolumeMount {
262363
mergedMountsMap := map[string]corev1.VolumeMount{}
263364
originalMounts := createVolumeMountMap(original)

pkg/util/merge/merge_test.go

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"reflect"
55
"testing"
66

7+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/probes"
8+
79
"k8s.io/apimachinery/pkg/api/resource"
810

911
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/container"
@@ -56,8 +58,6 @@ func TestMergeStringSlices(t *testing.T) {
5658
}
5759

5860
func TestMergeContainer(t *testing.T) {
59-
// TODO: Add additional fields as they are merged
60-
6161
defaultQuantity := resource.NewQuantity(int64(10), resource.DecimalExponent)
6262

6363
defaultContainer := container.New(
@@ -67,6 +67,16 @@ func TestMergeContainer(t *testing.T) {
6767
container.WithImagePullPolicy(corev1.PullAlways),
6868
container.WithWorkDir("default-work-dir"),
6969
container.WithArgs([]string{"arg0", "arg1"}),
70+
container.WithLivenessProbe(probes.Apply(
71+
probes.WithInitialDelaySeconds(10),
72+
probes.WithFailureThreshold(20),
73+
probes.WithExecCommand([]string{"exec", "command", "liveness"}),
74+
)),
75+
container.WithReadinessProbe(probes.Apply(
76+
probes.WithInitialDelaySeconds(20),
77+
probes.WithFailureThreshold(30),
78+
probes.WithExecCommand([]string{"exec", "command", "readiness"}),
79+
)),
7080
container.WithVolumeDevices([]corev1.VolumeDevice{
7181
{
7282
Name: "name-0",
@@ -121,6 +131,15 @@ func TestMergeContainer(t *testing.T) {
121131
container.WithImage("override-image"),
122132
container.WithWorkDir("override-work-dir"),
123133
container.WithArgs([]string{"arg3", "arg2"}),
134+
container.WithLivenessProbe(probes.Apply(
135+
probes.WithInitialDelaySeconds(15),
136+
probes.WithExecCommand([]string{"exec", "command", "override"}),
137+
)),
138+
container.WithReadinessProbe(probes.Apply(
139+
probes.WithInitialDelaySeconds(5),
140+
probes.WithFailureThreshold(6),
141+
probes.WithExecCommand([]string{"exec", "command", "readiness", "override"}),
142+
)),
124143
container.WithVolumeDevices([]corev1.VolumeDevice{
125144
{
126145
Name: "name-0",
@@ -180,6 +199,24 @@ func TestMergeContainer(t *testing.T) {
180199
assert.NotNil(t, mergedContainer.Env[1].ValueFrom)
181200
})
182201

202+
t.Run("Probes are overridden", func(t *testing.T) {
203+
t.Run("Liveness probe", func(t *testing.T) {
204+
livenessProbe := mergedContainer.LivenessProbe
205+
206+
assert.NotNil(t, livenessProbe)
207+
assert.Equal(t, int32(15), livenessProbe.InitialDelaySeconds, "value is specified in override and so should be used.")
208+
assert.Equal(t, int32(20), livenessProbe.FailureThreshold, "value is not specified in override so the original should be used.")
209+
assert.Equal(t, []string{"exec", "command", "override"}, livenessProbe.Exec.Command, "value is not specified in override so the original should be used.")
210+
})
211+
t.Run("Readiness probe", func(t *testing.T) {
212+
readinessProbe := mergedContainer.ReadinessProbe
213+
assert.NotNil(t, readinessProbe)
214+
assert.Equal(t, int32(5), readinessProbe.InitialDelaySeconds, "value is specified in override and so should be used.")
215+
assert.Equal(t, int32(6), readinessProbe.FailureThreshold, "value is not specified in override so the original should be used.")
216+
assert.Equal(t, []string{"exec", "command", "readiness", "override"}, readinessProbe.Exec.Command, "value is not specified in override so the original should be used.")
217+
})
218+
})
219+
183220
t.Run("Volume Devices are overridden", func(t *testing.T) {
184221
volumeDevices := mergedContainer.VolumeDevices
185222
assert.Len(t, volumeDevices, 3)
@@ -242,6 +279,24 @@ func TestMergeContainer(t *testing.T) {
242279
assert.Nil(t, mergedContainer.Env[1].ValueFrom)
243280
})
244281

282+
t.Run("Probes are not overridden", func(t *testing.T) {
283+
t.Run("Liveness probe", func(t *testing.T) {
284+
livenessProbe := mergedContainer.LivenessProbe
285+
286+
assert.NotNil(t, livenessProbe)
287+
assert.Equal(t, int32(10), livenessProbe.InitialDelaySeconds, "value is not specified in override so the original should be used.")
288+
assert.Equal(t, int32(20), livenessProbe.FailureThreshold, "value is not specified in override so the original should be used.")
289+
assert.Equal(t, []string{"exec", "command", "liveness"}, livenessProbe.Exec.Command, "value is not specified in override so the original should be used.")
290+
})
291+
t.Run("Readiness probe", func(t *testing.T) {
292+
readinessProbe := mergedContainer.ReadinessProbe
293+
assert.NotNil(t, readinessProbe)
294+
assert.Equal(t, int32(20), readinessProbe.InitialDelaySeconds, "value is not specified in override so the original should be used.")
295+
assert.Equal(t, int32(30), readinessProbe.FailureThreshold, "value is not specified in override so the original should be used.")
296+
assert.Equal(t, []string{"exec", "command", "readiness"}, readinessProbe.Exec.Command, "value is not specified in override so the original should be used.")
297+
})
298+
})
299+
245300
t.Run("Volume Devices are not overridden", func(t *testing.T) {
246301
volumeDevices := mergedContainer.VolumeDevices
247302
assert.Len(t, volumeDevices, 2)
@@ -445,3 +500,39 @@ func TestContainerPortSlicesByName(t *testing.T) {
445500
})
446501

447502
}
503+
504+
func TestMergeSecurityContext(t *testing.T) {
505+
privileged := true
506+
windowsRunAsUserName := "username"
507+
runAsGroup := int64(4)
508+
original := &corev1.SecurityContext{
509+
Capabilities: nil,
510+
Privileged: &privileged,
511+
WindowsOptions: &corev1.WindowsSecurityContextOptions{
512+
RunAsUserName: &windowsRunAsUserName,
513+
},
514+
RunAsGroup: &runAsGroup,
515+
}
516+
517+
runAsGroup = int64(6)
518+
override := &corev1.SecurityContext{
519+
Capabilities: &corev1.Capabilities{
520+
Add: []corev1.Capability{
521+
"123",
522+
"456",
523+
},
524+
},
525+
Privileged: &privileged,
526+
WindowsOptions: &corev1.WindowsSecurityContextOptions{
527+
RunAsUserName: &windowsRunAsUserName,
528+
},
529+
RunAsGroup: &runAsGroup,
530+
}
531+
532+
merged := SecurityContext(original, override)
533+
534+
assert.Equal(t, int64(6), *merged.RunAsGroup)
535+
assert.Equal(t, "username", *merged.WindowsOptions.RunAsUserName)
536+
assert.Equal(t, override.Capabilities, merged.Capabilities)
537+
assert.True(t, *override.Privileged)
538+
}

0 commit comments

Comments
 (0)