Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/v1/search/mongodbsearch_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ type MongoDBSearchStatus struct {
// +k8s:openapi-gen=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="Current state of the MongoDB deployment."
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.version",description="MongoDB Search version reconciled by the operator."
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The time since the MongoDB resource was created."
// +kubebuilder:resource:path=mongodbsearch,scope=Namespaced,shortName=mdbs
type MongoDBSearch struct {
Expand Down Expand Up @@ -142,6 +143,9 @@ func (s *MongoDBSearch) UpdateStatus(phase status.Phase, statusOptions ...status
if option, exists := status.GetOption(statusOptions, status.WarningsOption{}); exists {
s.Status.Warnings = append(s.Status.Warnings, option.(status.WarningsOption).Warnings...)
}
if option, exists := status.GetOption(statusOptions, MongoDBSearchVersionOption{}); exists {
s.Status.Version = option.(MongoDBSearchVersionOption).Version
}
}

func (s *MongoDBSearch) NamespacedName() types.NamespacedName {
Expand Down
17 changes: 17 additions & 0 deletions api/v1/search/status_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package search

import "github.com/mongodb/mongodb-kubernetes/api/v1/status"

type MongoDBSearchVersionOption struct {
Version string
}

var _ status.Option = MongoDBSearchVersionOption{}

func NewMongoDBSearchVersionOption(version string) MongoDBSearchVersionOption {
return MongoDBSearchVersionOption{Version: version}
}

func (o MongoDBSearchVersionOption) Value() interface{} {
return o.Version
}
7 changes: 7 additions & 0 deletions changelog/20251024_fix_mongodbsearch_status_version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: Surface reconciled MongoDBSearch version
kind: fix
date: 2025-10-24
---

* MongoDBSearch now records the reconciled mongot version in status and exposes it via a dedicated kubectl print column.
Comment on lines +1 to +7
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

40 changes: 37 additions & 3 deletions controllers/operator/mongodbsearch_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,12 @@ func TestMongoDBSearchReconcile_Success(t *testing.T) {
search.Spec.LogLevel = "WARN"

mdbc := newMongoDBCommunity("mdb", mock.TestNamespace)
reconciler, c := newSearchReconciler(mdbc, search)
operatorConfig := searchcontroller.OperatorSearchConfig{
SearchRepo: "testrepo",
SearchName: "mongot",
SearchVersion: "1.48.0",
}
reconciler, c := newSearchReconcilerWithOperatorConfig(mdbc, operatorConfig, search)

res, err := reconciler.Reconcile(
ctx,
Expand All @@ -182,6 +187,11 @@ func TestMongoDBSearchReconcile_Success(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, expected, res)

// BEFORE readiness: version should still be empty (controller sets Version only after StatefulSet ready)
searchPending := &searchv1.MongoDBSearch{}
assert.NoError(t, c.Get(ctx, types.NamespacedName{Name: search.Name, Namespace: search.Namespace}, searchPending))
assert.Empty(t, searchPending.Status.Version, "Status.Version must be empty before StatefulSet is marked ready")

svc := &corev1.Service{}
err = c.Get(ctx, search.SearchServiceNamespacedName(), svc)
assert.NoError(t, err)
Expand All @@ -194,9 +204,18 @@ func TestMongoDBSearchReconcile_Success(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, string(configYaml), cm.Data[searchcontroller.MongotConfigFilename])

sts := &appsv1.StatefulSet{}
err = c.Get(ctx, search.StatefulSetNamespacedName(), sts)
markStatefulSetReady(ctx, t, c, search.StatefulSetNamespacedName())

res, err = reconciler.Reconcile(
ctx,
reconcile.Request{NamespacedName: types.NamespacedName{Name: search.Name, Namespace: search.Namespace}},
)
assert.NoError(t, err)
assert.Equal(t, expected, res)

updatedSearch := &searchv1.MongoDBSearch{}
assert.NoError(t, c.Get(ctx, types.NamespacedName{Name: search.Name, Namespace: search.Namespace}, updatedSearch))
assert.Equal(t, operatorConfig.SearchVersion, updatedSearch.Status.Version)
}

func checkSearchReconcileFailed(
Expand Down Expand Up @@ -296,3 +315,18 @@ func TestMongoDBSearchReconcile_InvalidSearchImageVersion(t *testing.T) {
})
}
}

func markStatefulSetReady(ctx context.Context, t *testing.T, c client.Client, name types.NamespacedName) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t.Helper()

sts := &appsv1.StatefulSet{}
assert.NoError(t, c.Get(ctx, name, sts))

sts.Status.UpdatedReplicas = 1
sts.Status.ReadyReplicas = 1
sts.Status.CurrentReplicas = 1
sts.Status.Replicas = 1
sts.Status.ObservedGeneration = sts.Generation

assert.NoError(t, c.Status().Update(ctx, sts))
}
38 changes: 29 additions & 9 deletions controllers/searchcontroller/mongodbsearch_reconcile_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ func (r *MongoDBSearchReconcileHelper) reconcile(ctx context.Context, log *zap.S
return workflow.Failed(err)
}

if err := r.ValidateSearchImageVersion(); err != nil {
version := r.getMongotVersion()

if err := r.ValidateSearchImageVersion(version); err != nil {
return workflow.Failed(err)
}

Expand Down Expand Up @@ -137,7 +139,7 @@ func (r *MongoDBSearchReconcileHelper) reconcile(ctx context.Context, log *zap.S
return statefulSetStatus
}

return workflow.OK()
return workflow.OK().WithAdditionalOptions(searchv1.NewMongoDBSearchVersionOption(version))
}

func (r *MongoDBSearchReconcileHelper) ensureSourceKeyfile(ctx context.Context, log *zap.SugaredLogger) (statefulset.Modification, error) {
Expand Down Expand Up @@ -435,24 +437,23 @@ func (r *MongoDBSearchReconcileHelper) ValidateSingleMongoDBSearchForSearchSourc
return nil
}

func (r *MongoDBSearchReconcileHelper) ValidateSearchImageVersion() error {
version := r.getMongotImage()

func (r *MongoDBSearchReconcileHelper) ValidateSearchImageVersion(version string) error {
if strings.Contains(version, unsupportedSearchVersion) {
return xerrors.Errorf(unsupportedSearchVersionErrorFmt, unsupportedSearchVersion)
}

return nil
}

func (r *MongoDBSearchReconcileHelper) getMongotImage() string {
func (r *MongoDBSearchReconcileHelper) getMongotVersion() string {
version := strings.TrimSpace(r.mdbSearch.Spec.Version)
if version != "" {
return version
}

if r.operatorSearchConfig.SearchVersion != "" {
return r.operatorSearchConfig.SearchVersion
version = strings.TrimSpace(r.operatorSearchConfig.SearchVersion)
if version != "" {
return version
}

if r.mdbSearch.Spec.StatefulSetConfiguration == nil {
Expand All @@ -461,13 +462,32 @@ func (r *MongoDBSearchReconcileHelper) getMongotImage() string {

for _, container := range r.mdbSearch.Spec.StatefulSetConfiguration.SpecWrapper.Spec.Template.Spec.Containers {
if container.Name == MongotContainerName {
return container.Image
return extractImageTag(container.Image)
}
}

return ""
}

func extractImageTag(image string) string {
image = strings.TrimSpace(image)
if image == "" {
return ""
}

if at := strings.Index(image, "@"); at != -1 {
image = image[:at]
}

lastSlash := strings.LastIndex(image, "/")
lastColon := strings.LastIndex(image, ":")
if lastColon > lastSlash {
return image[lastColon+1:]
}

return ""
}

func SearchCoordinatorRole() mdbv1.MongoDBRole {
// direct translation of https://github.com/10gen/mongo/blob/6f8d95a513eea8f91ea9f5d895dd8a288dfcf725/src/mongo/db/auth/builtin_roles.yml#L652
return mdbv1.MongoDBRole{
Expand Down