@@ -19,6 +19,7 @@ import (
19
19
"math"
20
20
"regexp"
21
21
"slices"
22
+ "strconv"
22
23
23
24
ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1"
24
25
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
@@ -33,7 +34,8 @@ import (
33
34
svcapitypes "github.com/aws-controllers-k8s/rds-controller/apis/v1alpha1"
34
35
)
35
36
36
- var r = regexp .MustCompile (`[0-9]*$` )
37
+ // regex: 14.15 -> [14.15, 14, 15]
38
+ var versionRegex = regexp .MustCompile (`^(\d+)\.(\d+)` )
37
39
38
40
// customUpdate is required to fix
39
41
// https://github.com/aws-controllers-k8s/community/issues/917. The Input shape
@@ -97,6 +99,7 @@ func (rm *resourceManager) customUpdate(
97
99
// Merge in the information we read from the API call above to the copy of
98
100
// the original Kubernetes object we passed to the function
99
101
ko := desired .ko .DeepCopy ()
102
+ desiredEngineVersion := desired .ko .Spec .EngineVersion
100
103
101
104
if resp .DBCluster .ActivityStreamKinesisStreamName != nil {
102
105
ko .Status .ActivityStreamKinesisStreamName = resp .DBCluster .ActivityStreamKinesisStreamName
@@ -325,7 +328,16 @@ func (rm *resourceManager) customUpdate(
325
328
ko .Spec .EngineMode = nil
326
329
}
327
330
if resp .DBCluster .EngineVersion != nil {
328
- ko .Spec .EngineVersion = resp .DBCluster .EngineVersion
331
+ // special case during update do not patch
332
+ canUpdate , err := requireEngineVersionUpdate (desiredEngineVersion , resp .DBCluster .EngineVersion , * resp .DBCluster .AutoMinorVersionUpgrade )
333
+ if err != nil {
334
+ return nil , err
335
+ }
336
+ if desiredEngineVersion != nil && canUpdate {
337
+ ko .Spec .EngineVersion = desiredEngineVersion
338
+ } else {
339
+ ko .Spec .EngineVersion = resp .DBCluster .EngineVersion
340
+ }
329
341
} else {
330
342
ko .Spec .EngineVersion = nil
331
343
}
@@ -551,12 +563,29 @@ func (rm *resourceManager) newCustomUpdateRequestPayload(
551
563
res .EnableIAMDatabaseAuthentication = desired .ko .Spec .EnableIAMDatabaseAuthentication
552
564
}
553
565
if desired .ko .Spec .EngineVersion != nil && delta .DifferentAt ("Spec.EngineVersion" ) {
566
+ rlog := ackrtlog .FromContext (ctx )
554
567
autoMinorVersionUpgrade := true
555
568
if desired .ko .Spec .AutoMinorVersionUpgrade != nil {
556
569
autoMinorVersionUpgrade = * desired .ko .Spec .AutoMinorVersionUpgrade
557
570
}
558
- if requireEngineVersionUpdate (desired .ko .Spec .EngineVersion , latest .ko .Spec .EngineVersion , autoMinorVersionUpgrade ) {
571
+
572
+ shouldUpdate , err := requireEngineVersionUpdate (desired .ko .Spec .EngineVersion , latest .ko .Spec .EngineVersion , autoMinorVersionUpgrade )
573
+ if err != nil {
574
+ rlog .Debug ("error checking engine version update" , "error" , err .Error ())
575
+ // Don't return error, just don't include engine version in update
576
+ }
577
+
578
+ if shouldUpdate {
579
+ rlog .Debug ("including engine version in update" ,
580
+ "desired" , * desired .ko .Spec .EngineVersion ,
581
+ "latest" , * latest .ko .Spec .EngineVersion )
559
582
res .EngineVersion = desired .ko .Spec .EngineVersion
583
+ }
584
+
585
+ if ! shouldUpdate {
586
+ rlog .Debug ("skipping engine version update" ,
587
+ "desired" , * desired .ko .Spec .EngineVersion ,
588
+ "latest" , * latest .ko .Spec .EngineVersion )
560
589
}
561
590
}
562
591
if desired .ko .Spec .MasterUserPassword != nil && delta .DifferentAt ("Spec.MasterUserPassword" ) {
@@ -650,8 +679,71 @@ func getCloudwatchLogExportsConfigDifferences(cloudwatchLogExportsConfigDesired
650
679
return logsTypesToEnable , logsTypesToDisable
651
680
}
652
681
653
- func requireEngineVersionUpdate (desiredEngineVersion * string , latestEngineVersion * string , autoMinorVersionUpgrade bool ) bool {
654
- desiredMajorEngineVersion := r .ReplaceAllString (* desiredEngineVersion , "${1}" )
655
- latestMajorEngineVersion := r .ReplaceAllString (* latestEngineVersion , "${1}" )
656
- return ! autoMinorVersionUpgrade || desiredMajorEngineVersion != latestMajorEngineVersion
682
+ // requireEngineVersionUpdate determines whether an engine version update should be included
683
+ func requireEngineVersionUpdate (desiredEngineVersion * string , latestEngineVersion * string , autoMinorVersionUpgrade bool ) (bool , error ) {
684
+
685
+ // what should we do if
686
+ if latestEngineVersion == nil {
687
+ return false , fmt .Errorf ("engine versions cannot be nil" )
688
+ }
689
+
690
+ // Check if versions are already identical
691
+ if * desiredEngineVersion == * latestEngineVersion {
692
+ return false , nil
693
+ }
694
+
695
+ // Parse the desired version
696
+ // desiredMatches[0] is the entire string
697
+ desiredMatches := versionRegex .FindStringSubmatch (* desiredEngineVersion )
698
+ latestMatches := versionRegex .FindStringSubmatch (* latestEngineVersion )
699
+
700
+ // possible patterms with 2 or more dots....
701
+ if len (desiredMatches ) < 3 {
702
+ return false , fmt .Errorf ("invalid desired engine version format: %s" , * desiredEngineVersion )
703
+ }
704
+ if len (latestMatches ) < 3 {
705
+ return false , fmt .Errorf ("invalid latest engine version format: %s" , * latestEngineVersion )
706
+ }
707
+
708
+ desiredMajor , err := strconv .Atoi (desiredMatches [1 ])
709
+ if err != nil {
710
+ return false , fmt .Errorf ("invalid desired major version: %s" , desiredMatches [1 ])
711
+ }
712
+
713
+ desiredMinor , err := strconv .Atoi (desiredMatches [2 ])
714
+ if err != nil {
715
+ return false , fmt .Errorf ("invalid desired minor version: %s" , desiredMatches [2 ])
716
+ }
717
+
718
+ latestMajor , err := strconv .Atoi (latestMatches [1 ])
719
+ if err != nil {
720
+ return false , fmt .Errorf ("invalid latest major version: %s" , latestMatches [1 ])
721
+ }
722
+
723
+ latestMinor , err := strconv .Atoi (latestMatches [2 ])
724
+ if err != nil {
725
+ return false , fmt .Errorf ("invalid latest minor version: %s" , latestMatches [2 ])
726
+ }
727
+
728
+ // Check for downgrade attempts - this should raise an error to prevent
729
+ // confusing error messages
730
+ if desiredMajor < latestMajor || (desiredMajor == latestMajor && desiredMinor < latestMinor ) {
731
+ return false , fmt .Errorf ("downgrading engine version from %s to %s is not supported" ,
732
+ * latestEngineVersion , * desiredEngineVersion )
733
+ }
734
+
735
+ // Handle major version upgrades - always require explicit update
736
+ if desiredMajor > latestMajor {
737
+ return true , nil
738
+ }
739
+
740
+ // Handle minor version upgrades
741
+ if desiredMajor == latestMajor && desiredMinor > latestMinor {
742
+ // If autoMinorVersionUpgrade is false, we should explicitly handle minor version upgrades
743
+ // If true, AWS will handle it automatically
744
+ return ! autoMinorVersionUpgrade , nil
745
+ }
746
+
747
+ // Versions are identical (should have been caught above, but keeping as a safeguard)
748
+ return false , nil
657
749
}
0 commit comments