Skip to content

Commit 8100ed4

Browse files
authored
Add functionality for enterprise (#70)
1 parent ea21298 commit 8100ed4

File tree

9 files changed

+561
-41
lines changed

9 files changed

+561
-41
lines changed

pkg/controller/mongodb/mongodb_controller.go

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -419,12 +419,10 @@ func mongodbAgentContainer(volumeMounts []corev1.VolumeMount) container.Modifica
419419
"-serveStatusPort=5000",
420420
},
421421
),
422-
container.WithEnv(
423-
[]corev1.EnvVar{
424-
{
425-
Name: agentHealthStatusFilePathEnv,
426-
Value: agentHealthStatusFilePathValue,
427-
},
422+
container.WithEnvs(
423+
corev1.EnvVar{
424+
Name: agentHealthStatusFilePathEnv,
425+
Value: agentHealthStatusFilePathValue,
428426
},
429427
),
430428
)
@@ -461,16 +459,14 @@ mongod -f /data/automation-mongod.conf ;
461459
container.WithImage(fmt.Sprintf("mongo:%s", version)),
462460
container.WithResourceRequirements(resourcerequirements.Defaults()),
463461
container.WithCommand(mongoDbCommand),
464-
container.WithEnv(
465-
[]corev1.EnvVar{
466-
{
467-
Name: agentHealthStatusFilePathEnv,
468-
Value: "/healthstatus/agent-health-status.json",
469-
},
470-
{
471-
Name: preStopHookLogFilePathEnv,
472-
Value: "/hooks/pre-stop-hook.log",
473-
},
462+
container.WithEnvs(
463+
corev1.EnvVar{
464+
Name: agentHealthStatusFilePathEnv,
465+
Value: "/healthstatus/agent-health-status.json",
466+
},
467+
corev1.EnvVar{
468+
Name: preStopHookLogFilePathEnv,
469+
Value: "/hooks/pre-stop-hook.log",
474470
},
475471
),
476472
container.WithVolumeMounts(volumeMounts),

pkg/kube/container/container_test.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package container
2+
3+
import (
4+
"testing"
5+
6+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/lifecycle"
7+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/probes"
8+
"github.com/stretchr/testify/assert"
9+
corev1 "k8s.io/api/core/v1"
10+
)
11+
12+
func TestContainer(t *testing.T) {
13+
c := New(
14+
WithName("name"),
15+
WithImage("image"),
16+
WithImagePullPolicy(corev1.PullAlways),
17+
WithPorts([]corev1.ContainerPort{{Name: "port-1", ContainerPort: int32(1000)}}),
18+
WithSecurityContext(corev1.SecurityContext{
19+
RunAsGroup: int64Ref(100),
20+
RunAsNonRoot: boolRef(true),
21+
}),
22+
WithLifecycle(lifecycle.Apply(
23+
lifecycle.WithPrestopCommand([]string{"pre-stop-command"}),
24+
)),
25+
WithReadinessProbe(probes.Apply(
26+
probes.WithExecCommand([]string{"exec"}),
27+
probes.WithFailureThreshold(10),
28+
probes.WithPeriodSeconds(5),
29+
)),
30+
WithEnvs(
31+
[]corev1.EnvVar{
32+
{
33+
Name: "env-1",
34+
Value: "env-1-value",
35+
},
36+
}...,
37+
),
38+
)
39+
40+
assert.Equal(t, "name", c.Name)
41+
assert.Equal(t, "image", c.Image)
42+
assert.Equal(t, corev1.PullAlways, c.ImagePullPolicy)
43+
44+
assert.Len(t, c.Ports, 1)
45+
assert.Equal(t, int32(1000), c.Ports[0].ContainerPort)
46+
assert.Equal(t, "port-1", c.Ports[0].Name)
47+
48+
securityContext := c.SecurityContext
49+
assert.Equal(t, int64Ref(100), securityContext.RunAsGroup)
50+
assert.Equal(t, boolRef(true), securityContext.RunAsNonRoot)
51+
52+
readinessProbe := c.ReadinessProbe
53+
assert.Equal(t, int32(10), readinessProbe.FailureThreshold)
54+
assert.Equal(t, int32(5), readinessProbe.PeriodSeconds)
55+
assert.Equal(t, "exec", readinessProbe.Exec.Command[0])
56+
57+
lifeCycle := c.Lifecycle
58+
assert.NotNil(t, lifeCycle)
59+
assert.NotNil(t, lifeCycle.PreStop)
60+
assert.NotNil(t, lifeCycle.PreStop.Exec)
61+
assert.Equal(t, "pre-stop-command", lifeCycle.PreStop.Exec.Command[0])
62+
63+
assert.Len(t, c.Env, 1)
64+
assert.Equal(t, "env-1", c.Env[0].Name)
65+
assert.Equal(t, "env-1-value", c.Env[0].Value)
66+
}
67+
68+
func TestMergeEnvs(t *testing.T) {
69+
existing := []corev1.EnvVar{
70+
{
71+
Name: "C_env",
72+
Value: "C_value",
73+
},
74+
{
75+
Name: "B_env",
76+
Value: "B_value",
77+
},
78+
{
79+
Name: "A_env",
80+
Value: "A_value",
81+
},
82+
{
83+
Name: "F_env",
84+
ValueFrom: &corev1.EnvVarSource{
85+
SecretKeyRef: &corev1.SecretKeySelector{
86+
Key: "f_key",
87+
},
88+
},
89+
},
90+
}
91+
92+
desired := []corev1.EnvVar{
93+
{
94+
Name: "D_env",
95+
Value: "D_value",
96+
},
97+
{
98+
Name: "E_env",
99+
Value: "E_value",
100+
},
101+
{
102+
Name: "C_env",
103+
Value: "C_value_new",
104+
},
105+
{
106+
Name: "B_env",
107+
Value: "B_value_new",
108+
},
109+
{
110+
Name: "A_env",
111+
Value: "A_value",
112+
},
113+
}
114+
115+
merged := mergeEnvs(existing, desired)
116+
117+
t.Run("EnvVars should be sorted", func(t *testing.T) {
118+
assert.Equal(t, "A_env", merged[0].Name)
119+
assert.Equal(t, "B_env", merged[1].Name)
120+
assert.Equal(t, "C_env", merged[2].Name)
121+
assert.Equal(t, "D_env", merged[3].Name)
122+
assert.Equal(t, "E_env", merged[4].Name)
123+
assert.Equal(t, "F_env", merged[5].Name)
124+
})
125+
126+
t.Run("EnvVars of same name are updated", func(t *testing.T) {
127+
assert.Equal(t, "B_env", merged[1].Name)
128+
assert.Equal(t, "B_value_new", merged[1].Value)
129+
})
130+
131+
t.Run("Existing EnvVars are not touched", func(t *testing.T) {
132+
envVar := merged[5]
133+
assert.NotNil(t, envVar.ValueFrom)
134+
assert.Equal(t, "f_key", envVar.ValueFrom.SecretKeyRef.Key)
135+
})
136+
}
137+
138+
func boolRef(b bool) *bool {
139+
return &b
140+
}
141+
142+
func int64Ref(i int64) *int64 {
143+
return &i
144+
}

pkg/kube/container/containers.go

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,60 @@
11
package container
22

3-
import corev1 "k8s.io/api/core/v1"
3+
import (
4+
"sort"
5+
6+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/lifecycle"
7+
corev1 "k8s.io/api/core/v1"
8+
)
49

510
type Modification func(*corev1.Container)
611

12+
// Apply returns a function which applies a series of Modification functions to a *corev1.Container
13+
func Apply(modifications ...Modification) Modification {
14+
return func(container *corev1.Container) {
15+
for _, mod := range modifications {
16+
mod(container)
17+
}
18+
}
19+
}
20+
21+
// New returns a concrete corev1.Container instance which has been modified based on the provided
22+
// modifications
23+
func New(mods ...Modification) corev1.Container {
24+
c := corev1.Container{}
25+
for _, mod := range mods {
26+
mod(&c)
27+
}
28+
return c
29+
}
30+
31+
// NOOP is a valid Modification which applies no changes
32+
func NOOP() Modification {
33+
return func(container *corev1.Container) {}
34+
}
35+
36+
// WithName sets the container name
737
func WithName(name string) Modification {
838
return func(container *corev1.Container) {
939
container.Name = name
1040
}
1141
}
1242

43+
// WithImage sets the container image
1344
func WithImage(image string) Modification {
1445
return func(container *corev1.Container) {
1546
container.Image = image
1647
}
1748
}
1849

50+
// WithImagePullPolicy sets the container pullPolicy
1951
func WithImagePullPolicy(pullPolicy corev1.PullPolicy) Modification {
2052
return func(container *corev1.Container) {
2153
container.ImagePullPolicy = pullPolicy
2254
}
2355
}
2456

57+
// WithReadinessProbe modifies the container's Readiness Probe
2558
func WithReadinessProbe(probeFunc func(*corev1.Probe)) Modification {
2659
return func(container *corev1.Container) {
2760
if container.ReadinessProbe == nil {
@@ -31,24 +64,70 @@ func WithReadinessProbe(probeFunc func(*corev1.Probe)) Modification {
3164
}
3265
}
3366

67+
// WithLivenessProbe modifies the container's Liveness Probe
68+
func WithLivenessProbe(readinessProbeFunc func(*corev1.Probe)) Modification {
69+
return func(container *corev1.Container) {
70+
if container.LivenessProbe == nil {
71+
container.LivenessProbe = &corev1.Probe{}
72+
}
73+
readinessProbeFunc(container.LivenessProbe)
74+
}
75+
}
76+
77+
// WithResourceRequirements sets the container's Resources
3478
func WithResourceRequirements(resources corev1.ResourceRequirements) Modification {
3579
return func(container *corev1.Container) {
3680
container.Resources = resources
3781
}
3882
}
3983

84+
// WithCommand sets the containers Command
4085
func WithCommand(cmd []string) Modification {
4186
return func(container *corev1.Container) {
4287
container.Command = cmd
4388
}
4489
}
4590

46-
func WithEnv(envs []corev1.EnvVar) Modification {
91+
// WithLifecycle applies the lifecycle Modification to this container's
92+
// Lifecycle
93+
func WithLifecycle(lifeCycleMod lifecycle.Modification) Modification {
94+
return func(container *corev1.Container) {
95+
if container.Lifecycle == nil {
96+
container.Lifecycle = &corev1.Lifecycle{}
97+
}
98+
lifeCycleMod(container.Lifecycle)
99+
}
100+
}
101+
102+
// WithEnvs ensures all of the provided envs exist in the container
103+
func WithEnvs(envs ...corev1.EnvVar) Modification {
47104
return func(container *corev1.Container) {
48-
container.Env = envs
105+
container.Env = mergeEnvs(container.Env, envs)
106+
}
107+
}
108+
109+
func mergeEnvs(existing, desired []corev1.EnvVar) []corev1.EnvVar {
110+
envMap := make(map[string]corev1.EnvVar)
111+
for _, env := range existing {
112+
envMap[env.Name] = env
113+
}
114+
115+
for _, env := range desired {
116+
envMap[env.Name] = env
117+
}
118+
119+
var mergedEnv []corev1.EnvVar
120+
for _, env := range envMap {
121+
mergedEnv = append(mergedEnv, env)
49122
}
123+
124+
sort.SliceStable(mergedEnv, func(i, j int) bool {
125+
return mergedEnv[i].Name < mergedEnv[j].Name
126+
})
127+
return mergedEnv
50128
}
51129

130+
// WithVolumeMounts sets the VolumeMounts
52131
func WithVolumeMounts(volumeMounts []corev1.VolumeMount) Modification {
53132
volumesMountsCopy := make([]corev1.VolumeMount, len(volumeMounts))
54133
copy(volumesMountsCopy, volumeMounts)
@@ -57,10 +136,16 @@ func WithVolumeMounts(volumeMounts []corev1.VolumeMount) Modification {
57136
}
58137
}
59138

60-
func Apply(modifications ...Modification) Modification {
139+
// WithPorts sets the container's Ports
140+
func WithPorts(ports []corev1.ContainerPort) Modification {
61141
return func(container *corev1.Container) {
62-
for _, mod := range modifications {
63-
mod(container)
64-
}
142+
container.Ports = ports
143+
}
144+
}
145+
146+
// WithSecurityContext sets teh container's SecurityContext
147+
func WithSecurityContext(context corev1.SecurityContext) Modification {
148+
return func(container *corev1.Container) {
149+
container.SecurityContext = &context
65150
}
66151
}

pkg/kube/lifecycle/lifecyle.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package lifecycle
2+
3+
import corev1 "k8s.io/api/core/v1"
4+
5+
type Modification func(lifecycle *corev1.Lifecycle)
6+
7+
// Apply returns a function which applies a series of Modification functions to a *corev1.Lifecycle
8+
func Apply(modifications ...Modification) Modification {
9+
return func(lifecycle *corev1.Lifecycle) {
10+
for _, mod := range modifications {
11+
mod(lifecycle)
12+
}
13+
}
14+
}
15+
16+
// WithPrestopCommand sets the LifeCycles PreStop Exec Command
17+
func WithPrestopCommand(preStopCmd []string) Modification {
18+
return func(lc *corev1.Lifecycle) {
19+
if lc.PreStop == nil {
20+
lc.PreStop = &corev1.Handler{}
21+
}
22+
if lc.PreStop.Exec == nil {
23+
lc.PreStop.Exec = &corev1.ExecAction{}
24+
}
25+
lc.PreStop.Exec.Command = preStopCmd
26+
}
27+
}

0 commit comments

Comments
 (0)