Skip to content

Commit bd8ebf6

Browse files
authored
CLOUDP-69289: Fix custom StatefulSet spec merging of Env Vars (#140)
1 parent ed695f2 commit bd8ebf6

File tree

11 files changed

+113
-31
lines changed

11 files changed

+113
-31
lines changed

agent/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ RUN mkdir -p agent \
2121
&& rm -r mongodb-mms-automation-agent-*
2222

2323
RUN mkdir -p /var/lib/mongodb-mms-automation/probes/ \
24-
# && curl --retry 3 https://readinessprobe.s3-us-west-1.amazonaws.com/readinessprobe -o /var/lib/mongodb-mms-automation/probes/readinessprobe \
2524
&& curl --retry 3 https://readinessprobe-test.s3-us-west-1.amazonaws.com/readiness -o /var/lib/mongodb-mms-automation/probes/readinessprobe \
2625
&& chmod +x /var/lib/mongodb-mms-automation/probes/readinessprobe \
2726
&& mkdir -p /var/log/mongodb-mms-automation/ \
28-
&& chmod -R +wr /var/log/mongodb-mms-automation/
27+
&& chmod -R +wr /var/log/mongodb-mms-automation/ \
28+
# ensure that the agent user can write the logs in OpenShift
29+
&& touch /var/log/mongodb-mms-automation/readiness.log \
30+
&& chmod ugo+rw /var/log/mongodb-mms-automation/readiness.log
2931

3032
# Install MongoDB tools. The agent will automatically search the folder and find the binaries.
3133
RUN curl --fail --retry 3 --silent https://downloads.mongodb.org/tools/db/mongodb-database-tools-ubuntu1604-x86_64-${tools_version}.tgz -o mongodb-tools.tgz \

deploy/crds/mongodb.com_mongodb_crd.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ spec:
9191
- enabled
9292
type: object
9393
type: object
94+
statefulSet:
95+
description: StatefulSetConfiguration holds the optional custom StatefulSet
96+
that should be merged into the operator created one.
97+
type: object
9498
type:
9599
description: Type defines which type of MongoDB deployment the resource
96100
should create
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
apiVersion: mongodb.com/v1
2+
kind: MongoDB
3+
metadata:
4+
name: example-openshift-mongodb
5+
spec:
6+
members: 3
7+
type: ReplicaSet
8+
version: "4.2.6"
9+
statefulSet:
10+
spec:
11+
template:
12+
spec:
13+
containers:
14+
- name: "mongodb-agent"
15+
env:
16+
- name: MANAGED_SECURITY_CONTEXT
17+
value: "true"
18+
- name: "mongod"
19+
env:
20+
- name: MANAGED_SECURITY_CONTEXT
21+
value: "true"

deploy/operator.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ spec:
3434
value: quay.io/mongodb/mongodb-agent:10.15.1.6468-1
3535
- name: VERSION_UPGRADE_HOOK_IMAGE
3636
value: quay.io/mongodb/mongodb-kubernetes-operator-version-upgrade-post-start-hook:1.0.2
37+

pkg/apis/mongodb/v1/mongodb_types.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77

88
appsv1 "k8s.io/api/apps/v1"
9+
910
"k8s.io/apimachinery/pkg/runtime"
1011

1112
"k8s.io/apimachinery/pkg/types"
@@ -47,10 +48,10 @@ type MongoDBSpec struct {
4748

4849
// Users specifies the MongoDB users that should be configured in your deployment
4950
// +required
50-
Users []MongoDBUser `json:"users"`
51+
Users []MongoDBUser `json:"-"` // `json:"users"`
5152

5253
// +optional
53-
StatefulSetConfiguration StatefulSetConfiguration `json:"statefulset,omitempty"`
54+
StatefulSetConfiguration StatefulSetConfiguration `json:"statefulSet,omitempty"`
5455

5556
// AdditionalMongodConfig is additional configuration that can be passed to
5657
// each data-bearing mongod at runtime. Uses the same structure as the mongod
@@ -62,7 +63,8 @@ type MongoDBSpec struct {
6263
// StatefulSetConfiguration holds the optional custom StatefulSet
6364
// that should be merged into the operator created one.
6465
type StatefulSetConfiguration struct {
65-
Spec appsv1.StatefulSetSpec `json:"spec"`
66+
// The StatefulSet override options for underlying StatefulSet
67+
Spec appsv1.StatefulSetSpec `json:"spec"` // TODO: this pollutes the crd generation
6668
}
6769

6870
// MongodConfiguration holds the optional mongod configuration
@@ -130,7 +132,7 @@ type Role struct {
130132

131133
type Security struct {
132134
// +optional
133-
Authentication Authentication `json:"authentication"`
135+
Authentication Authentication `json:"-"` //`json:"authentication"`
134136
// TLS configuration for both client-server and server-server communication
135137
// +optional
136138
TLS TLS `json:"tls"`

pkg/controller/mongodb/replica_set_controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,8 @@ func getMongodConfigModification(mdb mdbv1.MongoDB) automationconfig.Modificatio
484484
return func(ac *automationconfig.AutomationConfig) {
485485
for i := range ac.Processes {
486486
// Mergo requires both objects to have the same type
487-
mergo.Merge(&ac.Processes[i].Args26, objx.New(mdb.Spec.AdditionalMongodConfig.Object), mergo.WithOverride)
487+
// TODO: proper error handling
488+
_ = mergo.Merge(&ac.Processes[i].Args26, objx.New(mdb.Spec.AdditionalMongodConfig.Object), mergo.WithOverride)
488489
}
489490
}
490491
}

pkg/kube/container/container_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
"testing"
66

7+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/util/envvar"
8+
79
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/resourcerequirements"
810

911
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/lifecycle"
@@ -132,7 +134,7 @@ func TestMergeEnvs(t *testing.T) {
132134
},
133135
}
134136

135-
merged := mergeEnvs(existing, desired)
137+
merged := envvar.MergeWithOverride(existing, desired)
136138

137139
t.Run("EnvVars should be sorted", func(t *testing.T) {
138140
assert.Equal(t, "A_env", merged[0].Name)

pkg/kube/container/containers.go

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"sort"
55
"strings"
66

7+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/util/envvar"
8+
79
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/lifecycle"
810
corev1 "k8s.io/api/core/v1"
911
)
@@ -103,31 +105,10 @@ func WithLifecycle(lifeCycleMod lifecycle.Modification) Modification {
103105
// WithEnvs ensures all of the provided envs exist in the container
104106
func WithEnvs(envs ...corev1.EnvVar) Modification {
105107
return func(container *corev1.Container) {
106-
container.Env = mergeEnvs(container.Env, envs)
108+
container.Env = envvar.MergeWithOverride(container.Env, envs)
107109
}
108110
}
109111

110-
func mergeEnvs(existing, desired []corev1.EnvVar) []corev1.EnvVar {
111-
envMap := make(map[string]corev1.EnvVar)
112-
for _, env := range existing {
113-
envMap[env.Name] = env
114-
}
115-
116-
for _, env := range desired {
117-
envMap[env.Name] = env
118-
}
119-
120-
var mergedEnv []corev1.EnvVar
121-
for _, env := range envMap {
122-
mergedEnv = append(mergedEnv, env)
123-
}
124-
125-
sort.SliceStable(mergedEnv, func(i, j int) bool {
126-
return mergedEnv[i].Name < mergedEnv[j].Name
127-
})
128-
return mergedEnv
129-
}
130-
131112
// WithVolumeMounts sets the VolumeMounts
132113
func WithVolumeMounts(volumeMounts []corev1.VolumeMount) Modification {
133114
volumesMountsCopy := make([]corev1.VolumeMount, len(volumeMounts))

pkg/kube/podtemplatespec/podspec_template.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package podtemplatespec
33
import (
44
"github.com/imdario/mergo"
55
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/container"
6+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/util/envvar"
67
corev1 "k8s.io/api/core/v1"
78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
89
)
@@ -297,11 +298,14 @@ func mergeContainers(defaultContainers, customContainers []corev1.Container) ([]
297298
for _, defaultContainer := range defaultContainers {
298299
if customContainer, ok := customMap[defaultContainer.Name]; ok {
299300
// The container is present in both maps, so we need to merge
300-
// Merge mounts
301+
// MergeWithOverride mounts
301302
mergedMounts, err := mergeVolumeMounts(defaultContainer.VolumeMounts, customContainer.VolumeMounts)
302303
if err != nil {
303304
return nil, err
304305
}
306+
307+
mergedEnvs := envvar.MergeWithOverride(defaultContainer.Env, customContainer.Env)
308+
305309
if err := mergo.Merge(&defaultContainer, customContainer, mergo.WithOverride); err != nil { //nolint
306310
return nil, err
307311
}
@@ -310,6 +314,7 @@ func mergeContainers(defaultContainers, customContainers []corev1.Container) ([]
310314
// to the defaulted limits
311315
defaultContainer.Resources = customContainer.Resources
312316
defaultContainer.VolumeMounts = mergedMounts
317+
defaultContainer.Env = mergedEnvs
313318
}
314319
// The default container was not modified by the override, so just add it
315320
mergedContainers = append(mergedContainers, defaultContainer)

pkg/kube/podtemplatespec/podspec_template_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,41 @@ func TestMultipleMerges(t *testing.T) {
182182
}
183183
}
184184

185+
func TestMergeEnvironmentVariables(t *testing.T) {
186+
otherDefaultContainer := getDefaultContainer()
187+
otherDefaultContainer.Env = append(otherDefaultContainer.Env, corev1.EnvVar{
188+
Name: "name1",
189+
Value: "val1",
190+
})
191+
192+
overrideOtherDefaultContainer := getDefaultContainer()
193+
overrideOtherDefaultContainer.Env = append(overrideOtherDefaultContainer.Env, corev1.EnvVar{
194+
Name: "name2",
195+
Value: "val2",
196+
})
197+
overrideOtherDefaultContainer.Env = append(overrideOtherDefaultContainer.Env, corev1.EnvVar{
198+
Name: "name1",
199+
Value: "changedValue",
200+
})
201+
202+
defaultSpec := getDefaultPodSpec()
203+
defaultSpec.Spec.Containers = []corev1.Container{otherDefaultContainer}
204+
205+
customSpec := getCustomPodSpec()
206+
customSpec.Spec.Containers = []corev1.Container{overrideOtherDefaultContainer}
207+
208+
mergedSpec, err := MergePodTemplateSpecs(defaultSpec, customSpec)
209+
assert.NoError(t, err)
210+
211+
mergedContainer := mergedSpec.Spec.Containers[0]
212+
213+
assert.Len(t, mergedContainer.Env, 2)
214+
assert.Equal(t, mergedContainer.Env[0].Name, "name1")
215+
assert.Equal(t, mergedContainer.Env[0].Value, "changedValue")
216+
assert.Equal(t, mergedContainer.Env[1].Name, "name2")
217+
assert.Equal(t, mergedContainer.Env[1].Value, "val2")
218+
}
219+
185220
func TestMergeContainer(t *testing.T) {
186221
vol0 := corev1.VolumeMount{Name: "container-0.volume-mount-0"}
187222
sideCarVol := corev1.VolumeMount{Name: "container-1.volume-mount-0"}

0 commit comments

Comments
 (0)