Skip to content

Commit c7e9fa5

Browse files
committed
add static and non static handling, change log file, re-add tests
1 parent 580aea5 commit c7e9fa5

File tree

6 files changed

+194
-17
lines changed

6 files changed

+194
-17
lines changed

changelog/20250806_breaking_changing_container_setup_of_static_architecture.md renamed to changelog/20250806_other_changing_container_setup_of_static_architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Changing container setup of static architecture
3-
kind: breaking
3+
kind: other
44
date: 2025-08-06
55
---
66

controllers/operator/mongodbshardedcluster_controller_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,7 @@ func computeSingleClusterShardOverridesFromDistribution(shardOverridesDistributi
17191719
}
17201720

17211721
type SingleClusterShardedScalingTestCase struct {
1722+
name string
17221723
scalingSteps []SingleClusterShardedScalingStep
17231724
}
17241725

@@ -1791,6 +1792,88 @@ func SingleClusterShardedScalingWithOverridesTestCase(t *testing.T, tc SingleClu
17911792
}
17921793
}
17931794

1795+
func TestSingleClusterShardedScalingWithOverrides(t *testing.T) {
1796+
scDefaultName := test.SCBuilderDefaultName + "-"
1797+
testCases := []SingleClusterShardedScalingTestCase{
1798+
{
1799+
name: "Basic sample test",
1800+
scalingSteps: []SingleClusterShardedScalingStep{
1801+
{
1802+
name: "Initial scaling",
1803+
shardCount: 3,
1804+
mongodsPerShardCount: 3,
1805+
shardOverrides: map[string]int{
1806+
scDefaultName + "0": 5,
1807+
},
1808+
expectedShardDistribution: []int{
1809+
5,
1810+
3,
1811+
3,
1812+
},
1813+
},
1814+
{
1815+
name: "Scale up mongodsPerShard",
1816+
shardCount: 3,
1817+
mongodsPerShardCount: 5,
1818+
shardOverrides: map[string]int{
1819+
scDefaultName + "0": 5,
1820+
},
1821+
expectedShardDistribution: []int{
1822+
5,
1823+
5,
1824+
5,
1825+
},
1826+
},
1827+
},
1828+
},
1829+
{
1830+
// This operation works in unit test only
1831+
// In e2e tests, the operator is waiting for uncreated hostnames to be ready
1832+
name: "Scale overrides up and down",
1833+
scalingSteps: []SingleClusterShardedScalingStep{
1834+
{
1835+
name: "Initial deployment",
1836+
shardCount: 4,
1837+
mongodsPerShardCount: 2,
1838+
shardOverrides: map[string]int{
1839+
scDefaultName + "0": 3,
1840+
scDefaultName + "1": 3,
1841+
scDefaultName + "3": 2,
1842+
},
1843+
expectedShardDistribution: []int{
1844+
3,
1845+
3,
1846+
2, // Not overridden
1847+
2,
1848+
},
1849+
},
1850+
{
1851+
name: "Scale overrides",
1852+
shardCount: 4,
1853+
mongodsPerShardCount: 2,
1854+
shardOverrides: map[string]int{
1855+
scDefaultName + "0": 2, // Scaled down
1856+
scDefaultName + "1": 2, // Scaled down
1857+
scDefaultName + "3": 3, // Scaled up
1858+
},
1859+
expectedShardDistribution: []int{
1860+
2, // Scaled down
1861+
2, // Scaled down
1862+
2, // Not overridden
1863+
3, // Scaled up
1864+
},
1865+
},
1866+
},
1867+
},
1868+
}
1869+
1870+
for _, tc := range testCases {
1871+
t.Run(tc.name, func(t *testing.T) {
1872+
SingleClusterShardedScalingWithOverridesTestCase(t, tc)
1873+
})
1874+
}
1875+
}
1876+
17941877
func generateHostsWithDistributionSingleCluster(stsName string, namespace string, memberCount int, clusterDomain string, externalClusterDomain string) ([]string, []string) {
17951878
var hosts []string
17961879
var podNames []string

lib/sonar/builders/docker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def inner_docker_push(should_raise=False):
209209
# Instead of doing the hack here, we should instead either:
210210
# - make sonar aware of context images
211211
# - move the logic out of sonar to pipeline.py to all the places where we build context images
212-
if "-context" in tag and image_exists(registry, tag) and "ecr" not in registry:
212+
if "-context" in tag and image_exists(registry, tag):
213213
logger.info(f"Image: {tag} in registry: {registry} already exists skipping pushing it")
214214
else:
215215
logger.info("Image does not exist remotely or is not a context image, pushing it!")

mongodb-community-operator/controllers/construct/build_statefulset_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func TestManagedSecurityContext(t *testing.T) {
6565

6666
func TestMongod_Container(t *testing.T) {
6767
const mongodbImageMock = "fake-mongodbImage"
68-
c := container.New(mongodbContainer(mongodbImageMock, []corev1.VolumeMount{}, mdbv1.NewMongodConfiguration()))
68+
c := container.New(mongodbContainer(mongodbImageMock, []corev1.VolumeMount{}, mdbv1.NewMongodConfiguration(), false))
6969

7070
t.Run("Has correct Env vars", func(t *testing.T) {
7171
assert.Len(t, c.Env, 1)

mongodb-community-operator/controllers/construct/mongodbstatefulset.go

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func BuildMongoDBReplicaSetStatefulSetModificationFunction(mdb MongoDBStatefulSe
244244
podtemplatespec.WithVolume(keyFileVolume),
245245
podtemplatespec.WithServiceAccount(mongodbDatabaseServiceAccountName),
246246
podtemplatespec.WithContainer(AgentName, mongodbAgentContainer(mdb.AutomationConfigSecretName(), mongodbAgentVolumeMounts, agentLogLevel, agentLogFile, agentMaxLogFileDurationHours, agentImage)),
247-
podtemplatespec.WithContainer(MongodbName, mongodbContainer(mongodbImage, mongodVolumeMounts, mdb.GetMongodConfiguration())),
247+
podtemplatespec.WithContainer(MongodbName, mongodbContainer(mongodbImage, mongodVolumeMounts, mdb.GetMongodConfiguration(), !withInitContainers)),
248248
withStaticContainerModification,
249249
upgradeInitContainer,
250250
readinessInitContainer,
@@ -400,24 +400,64 @@ func readinessProbeInit(volumeMount []corev1.VolumeMount, readinessProbeImage st
400400
)
401401
}
402402

403-
func mongodbContainer(mongodbImage string, volumeMounts []corev1.VolumeMount, additionalMongoDBConfig mdbv1.MongodConfiguration) container.Modification {
404-
filePath := additionalMongoDBConfig.GetDBDataDir() + "/" + automationMongodConfFileName
405-
mongoDbCommand := fmt.Sprintf(`
406-
# Signal handler for graceful shutdown
403+
// buildSignalHandling returns the signal handling setup for static architecture
404+
func buildSignalHandling() string {
405+
return fmt.Sprintf(`
406+
# Signal handler for graceful shutdown in shared PID namespace
407407
cleanup() {
408+
# Important! Keep this in sync with DefaultPodTerminationPeriodSeconds constant from constants.go
409+
termination_timeout_seconds=%d
410+
408411
echo "MongoDB container received SIGTERM, shutting down gracefully..."
412+
409413
if [ -n "$MONGOD_PID" ] && kill -0 "$MONGOD_PID" 2>/dev/null; then
410414
echo "Sending SIGTERM to mongod process $MONGOD_PID"
411-
kill -TERM "$MONGOD_PID"
412-
wait "$MONGOD_PID"
415+
kill -15 "$MONGOD_PID"
416+
417+
echo "Waiting until mongod process is shutdown. Note, that if mongod process fails to shutdown in the time specified by the 'terminationGracePeriodSeconds' property (default ${termination_timeout_seconds} seconds) then the container will be killed by Kubernetes."
418+
419+
# Use the same robust waiting mechanism as agent-launcher-lib.sh
420+
# We cannot use 'wait' for processes started in background, use spinning loop
421+
while [ -e "/proc/${MONGOD_PID}" ]; do
422+
sleep 0.1
423+
done
424+
413425
echo "mongod process has exited"
414426
fi
427+
428+
echo "MongoDB container shutdown complete"
415429
exit 0
416430
}
417431
418-
# Set up signal handler
432+
# Set up signal handler for static architecture
419433
trap cleanup SIGTERM
434+
`, util.DefaultPodTerminationPeriodSeconds)
435+
}
436+
437+
// buildMongodExecution returns the mongod execution command based on architecture
438+
// in static we run /pause as pid1 and we need to ensure to redirect sigterm to the mongod process
439+
func buildMongodExecution(filePath string, isStatic bool) string {
440+
if isStatic {
441+
return fmt.Sprintf(`mongod -f %s &
442+
MONGOD_PID=$!
443+
echo "Started mongod with PID $MONGOD_PID"
420444
445+
# Wait for mongod to finish
446+
wait "$MONGOD_PID"`, filePath)
447+
}
448+
return fmt.Sprintf("exec mongod -f %s", filePath)
449+
}
450+
451+
// buildMongodbCommand constructs the complete MongoDB container command
452+
func buildMongodbCommand(filePath string, isStatic bool) string {
453+
signalHandling := ""
454+
if isStatic {
455+
signalHandling = buildSignalHandling()
456+
}
457+
458+
mongodExec := buildMongodExecution(filePath, isStatic)
459+
460+
return fmt.Sprintf(`%s
421461
if [ -e "/hooks/version-upgrade" ]; then
422462
#run post-start hook to handle version changes (if exists)
423463
/hooks/version-upgrade
@@ -437,13 +477,13 @@ sleep 15
437477
438478
# start mongod with this configuration
439479
echo "Starting mongod..."
440-
mongod -f %s &
441-
MONGOD_PID=$!
442-
echo "Started mongod with PID $MONGOD_PID"
480+
%s
481+
`, signalHandling, filePath, keyfileFilePath, mongodExec)
482+
}
443483

444-
# Wait for mongod to finish
445-
wait "$MONGOD_PID"
446-
`, filePath, keyfileFilePath, filePath)
484+
func mongodbContainer(mongodbImage string, volumeMounts []corev1.VolumeMount, additionalMongoDBConfig mdbv1.MongodConfiguration, isStatic bool) container.Modification {
485+
filePath := additionalMongoDBConfig.GetDBDataDir() + "/" + automationMongodConfFileName
486+
mongoDbCommand := buildMongodbCommand(filePath, isStatic)
447487

448488
containerCommand := []string{
449489
"/bin/sh",

mongodb-community-operator/controllers/construct/mongodbstatefulset_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
corev1 "k8s.io/api/core/v1"
99

10+
mdbv1 "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/api/v1"
1011
"github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/readiness/config"
1112
)
1213

@@ -98,3 +99,56 @@ func TestCollectEnvVars(t *testing.T) {
9899
})
99100
}
100101
}
102+
103+
func TestMongodbContainer_SignalHandling(t *testing.T) {
104+
tests := []struct {
105+
name string
106+
isStatic bool
107+
wantExec bool
108+
}{
109+
{
110+
name: "Non-static architecture uses exec mongod",
111+
isStatic: false,
112+
wantExec: true,
113+
},
114+
{
115+
name: "Static architecture uses trap and background mongod",
116+
isStatic: true,
117+
wantExec: false,
118+
},
119+
}
120+
121+
for _, tt := range tests {
122+
t.Run(tt.name, func(t *testing.T) {
123+
mongodConfig := mdbv1.NewMongodConfiguration()
124+
mongodConfig.SetOption("storage.dbPath", "/data")
125+
126+
containerMod := mongodbContainer("test-image", []corev1.VolumeMount{}, mongodConfig, tt.isStatic)
127+
128+
testContainer := &corev1.Container{}
129+
containerMod(testContainer)
130+
131+
assert.Len(t, testContainer.Command, 3)
132+
assert.Equal(t, "/bin/sh", testContainer.Command[0])
133+
assert.Equal(t, "-c", testContainer.Command[1])
134+
commandScript := testContainer.Command[2]
135+
136+
if tt.isStatic {
137+
assert.Contains(t, commandScript, "trap cleanup SIGTERM", "Static architecture should include signal trap")
138+
assert.Contains(t, commandScript, "cleanup() {", "Static architecture should include cleanup function")
139+
assert.Contains(t, commandScript, "mongod -f /data/automation-mongod.conf &", "Static architecture should run mongod in background")
140+
assert.Contains(t, commandScript, "wait \"$MONGOD_PID\"", "Static architecture should wait for mongod process")
141+
assert.Contains(t, commandScript, "termination_timeout_seconds", "Static architecture should include timeout configuration")
142+
assert.Contains(t, commandScript, "while [ -e \"/proc/${MONGOD_PID}\" ]", "Static architecture should include robust process waiting")
143+
assert.Contains(t, commandScript, "kill -15 \"$MONGOD_PID\"", "Static architecture should send SIGTERM to mongod")
144+
} else {
145+
assert.NotContains(t, commandScript, "trap cleanup SIGTERM", "Non-static architecture should not include signal trap")
146+
assert.NotContains(t, commandScript, "cleanup() {", "Non-static architecture should not include cleanup function")
147+
assert.Contains(t, commandScript, "exec mongod -f /data/automation-mongod.conf", "Non-static architecture should exec mongod")
148+
}
149+
150+
assert.Contains(t, commandScript, "Waiting for config and keyfile files to be created by the agent", "Should wait for agent files")
151+
assert.Contains(t, commandScript, "Starting mongod...", "Should start mongod")
152+
})
153+
}
154+
}

0 commit comments

Comments
 (0)