Skip to content

Commit f1e1c5a

Browse files
authored
Show FCV validation error before applying changes (#621)
# Summary Previously applying the faulty `spec.featureCompatibilityVersion`, but having two items separated by "." would make validation pass. Example of incorrect, but allowed `spec.featureCompatibilityVersion` - "8.abc". Fortunately, later during [AC update](https://github.com/mongodb/mongodb-kubernetes/blob/f0050b8942545701e8cb9e42d54d14f0cb58ee6a/mongodb-community-operator/pkg/automationconfig/automation_config_builder.go#L271-L276) we would still throw an error, but the changes would be applied. https://github.com/mongodb/mongodb-kubernetes/blob/f0050b8942545701e8cb9e42d54d14f0cb58ee6a/mongodb-community-operator/pkg/automationconfig/automation_config_builder.go#L271-L276 This change adds the missing semver validation for `spec.featureCompatibilityVersion` (including appDB). ## Proof of Work Passing updated unit tests. ## Checklist - [ ] Have you linked a jira ticket and/or is the ticket in the title? - [x] Have you checked whether your jira ticket required DOCSP changes? - [x] Have you added changelog file? - use `skip-changelog` label if not needed - refer to [Changelog files and Release Notes](https://github.com/mongodb/mongodb-kubernetes/blob/master/CONTRIBUTING.md#changelog-files-and-release-notes) section in CONTRIBUTING.md for more details
1 parent 1577d40 commit f1e1c5a

File tree

7 files changed

+47
-15
lines changed

7 files changed

+47
-15
lines changed

api/v1/mdb/mongodb_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,10 +688,10 @@ func (m *MongoDbSpec) GetClusterDomain() string {
688688

689689
func (m *MongoDbSpec) MinimumMajorVersion() uint64 {
690690
if m.FeatureCompatibilityVersion != nil && *m.FeatureCompatibilityVersion != "" {
691-
fcv := *m.FeatureCompatibilityVersion
691+
fcvString := *m.FeatureCompatibilityVersion
692692

693693
// ignore errors here as the format of FCV/version is handled by CRD validation
694-
semverFcv, _ := semver.Make(fmt.Sprintf("%s.0", fcv))
694+
semverFcv, _ := fcv.FeatureCompatibilityVersionToSemverFormat(fcvString)
695695
return semverFcv.Major
696696
}
697697
semverVersion, _ := semver.Make(m.GetMongoDBVersion())

api/v1/mdb/mongodb_validation.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
v1 "github.com/mongodb/mongodb-kubernetes/api/v1"
1414
"github.com/mongodb/mongodb-kubernetes/api/v1/status"
15+
"github.com/mongodb/mongodb-kubernetes/pkg/fcv"
1516
"github.com/mongodb/mongodb-kubernetes/pkg/multicluster"
1617
"github.com/mongodb/mongodb-kubernetes/pkg/util"
1718
"github.com/mongodb/mongodb-kubernetes/pkg/util/stringutil"
@@ -430,15 +431,21 @@ func featureCompatibilityVersionValidation(d DbCommonSpec) v1.ValidationResult {
430431
return ValidateFCV(fcv)
431432
}
432433

433-
func ValidateFCV(fcv *string) v1.ValidationResult {
434-
if fcv != nil {
435-
f := *fcv
436-
if f == util.AlwaysMatchVersionFCV {
434+
func ValidateFCV(fcvStringPointer *string) v1.ValidationResult {
435+
if fcvStringPointer != nil {
436+
fcvString := *fcvStringPointer
437+
if fcvString == util.AlwaysMatchVersionFCV {
437438
return v1.ValidationSuccess()
438439
}
439-
splitted := strings.Split(f, ".")
440+
441+
splitted := strings.Split(fcvString, ".")
440442
if len(splitted) != 2 {
441-
return v1.ValidationError("invalid feature compatibility version: %s, possible values are: '%s' or 'major.minor'", f, util.AlwaysMatchVersionFCV)
443+
return v1.ValidationError("invalid feature compatibility version %q, possible values are: '%s' or 'major.minor'", fcvString, util.AlwaysMatchVersionFCV)
444+
}
445+
446+
_, err := fcv.FeatureCompatibilityVersionToSemverFormat(fcvString)
447+
if err != nil {
448+
return v1.ValidationError("invalid feature compatibility version %q: %s", fcvString, err)
442449
}
443450
}
444451
return v1.ValidationResult{}

api/v1/mdb/mongodb_validation_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func TestReplicasetFCV(t *testing.T) {
244244
name: "Invalid FCV value",
245245
fcv: ptr.To("test"),
246246
expectError: true,
247-
expectedErrorMessage: "invalid feature compatibility version: test, possible values are: 'AlwaysMatchVersion' or 'major.minor'",
247+
expectedErrorMessage: "invalid feature compatibility version \"test\", possible values are: 'AlwaysMatchVersion' or 'major.minor'",
248248
},
249249
{
250250
name: "Valid FCV with specific version",
@@ -255,7 +255,13 @@ func TestReplicasetFCV(t *testing.T) {
255255
name: "Invalid FCV - not major.minor only",
256256
fcv: ptr.To("4.0.0"),
257257
expectError: true,
258-
expectedErrorMessage: "invalid feature compatibility version: 4.0.0, possible values are: 'AlwaysMatchVersion' or 'major.minor'",
258+
expectedErrorMessage: "invalid feature compatibility version \"4.0.0\", possible values are: 'AlwaysMatchVersion' or 'major.minor'",
259+
},
260+
{
261+
name: "Invalid FCV - not major leading 0",
262+
fcv: ptr.To("4.01"),
263+
expectError: true,
264+
expectedErrorMessage: "invalid feature compatibility version \"4.01\": Minor number must not contain leading zeroes \"01\"",
259265
},
260266
{
261267
name: "Valid FCV with AlwaysMatchVersion",

api/v1/mdbmulti/mongodb_multi_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -535,10 +535,10 @@ func (m *MongoDBMultiSpec) GetMemberOptions() []automationconfig.MemberOptions {
535535

536536
func (m *MongoDBMultiSpec) MinimumMajorVersion() uint64 {
537537
if m.FeatureCompatibilityVersion != nil && *m.FeatureCompatibilityVersion != "" {
538-
fcv := *m.FeatureCompatibilityVersion
538+
fcvString := *m.FeatureCompatibilityVersion
539539

540540
// ignore errors here as the format of FCV/version is handled by CRD validation
541-
semverFcv, _ := semver.Make(fmt.Sprintf("%s.0", fcv))
541+
semverFcv, _ := fcv.FeatureCompatibilityVersionToSemverFormat(fcvString)
542542
return semverFcv.Major
543543
}
544544
semverVersion, _ := semver.Make(m.GetMongoDBVersion())
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
kind: fix
3+
date: 2025-12-02
4+
---
5+
6+
* **MongoDB**, **MongoDBOpsManager**: Improve validation for `featureCompatibilityVersion` field in `MongoDB` and `MongoDBOpsManager` spec.
7+
The field now enforces proper semantic versioning. Previously, invalid semver values could be accepted,
8+
potentially resulting in incorrect configurations.

controllers/om/deployment.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
mdbv1 "github.com/mongodb/mongodb-kubernetes/api/v1/mdb"
1616
mdbcv1 "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/api/v1"
1717
"github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/automationconfig"
18+
"github.com/mongodb/mongodb-kubernetes/pkg/fcv"
1819
"github.com/mongodb/mongodb-kubernetes/pkg/tls"
1920
"github.com/mongodb/mongodb-kubernetes/pkg/util"
2021
"github.com/mongodb/mongodb-kubernetes/pkg/util/maputil"
@@ -493,8 +494,8 @@ func (d Deployment) MinimumMajorVersion() uint64 {
493494
minimumMajorVersion := semver.Version{Major: math.MaxUint64}
494495
for _, p := range d.getProcesses() {
495496
if p.FeatureCompatibilityVersion() != "" {
496-
fcv := fmt.Sprintf("%s.0", util.StripEnt(p.FeatureCompatibilityVersion()))
497-
semverFcv, _ := semver.Make(fcv)
497+
fcvString := util.StripEnt(p.FeatureCompatibilityVersion())
498+
semverFcv, _ := fcv.FeatureCompatibilityVersionToSemverFormat(fcvString)
498499
if semverFcv.LE(minimumMajorVersion) {
499500
minimumMajorVersion = semverFcv
500501
}

pkg/fcv/fcv.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package fcv
22

3-
import "github.com/mongodb/mongodb-kubernetes/pkg/util"
3+
import (
4+
"fmt"
5+
6+
"github.com/blang/semver"
7+
8+
"github.com/mongodb/mongodb-kubernetes/pkg/util"
9+
)
410

511
func CalculateFeatureCompatibilityVersion(currentVersionFromCR string, fcvFromStatus string, fcvFromCR *string) string {
612
majorMinorVersionFromCR, setVersion, _ := util.MajorMinorVersion(currentVersionFromCR)
@@ -44,3 +50,7 @@ func CalculateFeatureCompatibilityVersion(currentVersionFromCR string, fcvFromSt
4450

4551
return lastAppliedMajorMinorVersion
4652
}
53+
54+
func FeatureCompatibilityVersionToSemverFormat(fcv string) (semver.Version, error) {
55+
return semver.Make(fmt.Sprintf("%s.0", fcv))
56+
}

0 commit comments

Comments
 (0)