Skip to content

Commit 4d26ded

Browse files
committed
fix patching spec during an update
1 parent 786ab7c commit 4d26ded

File tree

1 file changed

+99
-7
lines changed

1 file changed

+99
-7
lines changed

pkg/resource/db_cluster/custom_update.go

+99-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"math"
2020
"regexp"
2121
"slices"
22+
"strconv"
2223

2324
ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1"
2425
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
@@ -33,7 +34,8 @@ import (
3334
svcapitypes "github.com/aws-controllers-k8s/rds-controller/apis/v1alpha1"
3435
)
3536

36-
var r = regexp.MustCompile(`[0-9]*$`)
37+
// regex: 14.15 -> [14.15, 14, 15]
38+
var versionRegex = regexp.MustCompile(`^(\d+)\.(\d+)`)
3739

3840
// customUpdate is required to fix
3941
// https://github.com/aws-controllers-k8s/community/issues/917. The Input shape
@@ -97,6 +99,7 @@ func (rm *resourceManager) customUpdate(
9799
// Merge in the information we read from the API call above to the copy of
98100
// the original Kubernetes object we passed to the function
99101
ko := desired.ko.DeepCopy()
102+
desiredEngineVersion := desired.ko.Spec.EngineVersion
100103

101104
if resp.DBCluster.ActivityStreamKinesisStreamName != nil {
102105
ko.Status.ActivityStreamKinesisStreamName = resp.DBCluster.ActivityStreamKinesisStreamName
@@ -325,7 +328,16 @@ func (rm *resourceManager) customUpdate(
325328
ko.Spec.EngineMode = nil
326329
}
327330
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+
}
329341
} else {
330342
ko.Spec.EngineVersion = nil
331343
}
@@ -551,12 +563,29 @@ func (rm *resourceManager) newCustomUpdateRequestPayload(
551563
res.EnableIAMDatabaseAuthentication = desired.ko.Spec.EnableIAMDatabaseAuthentication
552564
}
553565
if desired.ko.Spec.EngineVersion != nil && delta.DifferentAt("Spec.EngineVersion") {
566+
rlog := ackrtlog.FromContext(ctx)
554567
autoMinorVersionUpgrade := true
555568
if desired.ko.Spec.AutoMinorVersionUpgrade != nil {
556569
autoMinorVersionUpgrade = *desired.ko.Spec.AutoMinorVersionUpgrade
557570
}
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)
559582
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)
560589
}
561590
}
562591
if desired.ko.Spec.MasterUserPassword != nil && delta.DifferentAt("Spec.MasterUserPassword") {
@@ -650,8 +679,71 @@ func getCloudwatchLogExportsConfigDifferences(cloudwatchLogExportsConfigDesired
650679
return logsTypesToEnable, logsTypesToDisable
651680
}
652681

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
657749
}

0 commit comments

Comments
 (0)