Repository: crossplane-contrib/provider-aws (go)

Issue:
ReplicationGroup engineVersion check invalid - 0.49.1
### What happened?
This PR https://github.com/crossplane-contrib/provider-aws/pull/1985 i believe results in a bug where existing redis clusters with `engineVersion: 6.x` set are failing to sync due to the check for version trying to cast x to an int in 6.x despite it being a completely valid option. Curiously creating a new replicationgroup with `6.x` doesn't have any issues as of yet 

### How can we reproduce it?
1) Have an existing replicationgroup as so (obviously switch out the bits required for your test env)
```
apiVersion: cache.aws.crossplane.io/v1beta1
kind: ReplicationGroup
metadata:
  name: test-redis
spec:
  deletionPolicy: Delete
  forProvider:
    applyModificationsImmediately: true
    atRestEncryptionEnabled: true
    authEnabled: true
    automaticFailoverEnabled: true
    cacheNodeType: cache.t3.micro
    cacheParameterGroupName: default.redis6.x
    cacheSubnetGroupName: lab-data
    engine: redis
    engineVersion: 6.x
    multiAZEnabled: false
    numCacheClusters: 2
    preferredMaintenanceWindow: wed:22:00-wed:23:00
    region: eu-west-1
    replicationGroupDescription: test
    securityGroupIdRefs:
      - name: lab-elasticache-sg
    securityGroupIds:
      - sg-0f0506ae5e242fa6fe
    snapshotRetentionLimit: 0
    snapshotWindow: 01:00-02:00
    transitEncryptionEnabled: true
  managementPolicies:
    - '*'
  providerConfigRef:
    name: aws
  writeConnectionSecretToRef:
    name: test
    namespace: flux-system
```

2) Upgrade to 0.49.1 (in theory anything after 0.47.0 probably has this issue)

3) Observe existing elasticache resource has error as so
```
    - lastTransitionTime: '2024-08-21T09:15:00Z'
      message: >-
        update failed: unable to parse version number: unable to parse version
        number: strconv.Atoi: parsing "x": invalid syntax
      reason: ReconcileError
      status: 'False'
      type: Synced
```

### What environment did it happen in?
Crossplane version: 1.16.0
Provider version: 0.49.1
K8s Distro: EKS
K8s Version: 1.30


Pull request:
chore: attempt to fix the version problems of replicationgroups (#1)
### Description of your changes

<!--
This PR hopefully fixes the issue where replicationGroup objects with engineVersion 6.x start to fail sync post upgrade 0.47.0 It does this by allowing for versions to end in `.x` and handle that as a string rather than trying to cast it to an int

-->
Fixes #2087

I have:

- [x] Read and followed Crossplane's [contribution process].
- [x] Run `make reviewable test` to ensure this PR is ready for review. <--- Doesn't exist and the crossplane contribution guide seems specific for the crossplane core



Patch:
diff --git a/pkg/controller/cache/replicationgroup/managed.go b/pkg/controller/cache/replicationgroup/managed.go
index 2df8d5be9..c01282d31 100644
--- a/pkg/controller/cache/replicationgroup/managed.go
+++ b/pkg/controller/cache/replicationgroup/managed.go
@@ -364,11 +364,15 @@ func getVersion(version *string) (*string, error) {
 	}
 	versionOut := strconv.Itoa(version1)
 	if len(versionSplit) > 1 {
-		version2, err := strconv.Atoi(versionSplit[1])
-		if err != nil {
-			return nil, errors.Wrap(err, errVersionInput)
+		if versionSplit[1] != "x" {
+			version2, err := strconv.Atoi(versionSplit[1])
+			if err != nil {
+				return nil, errors.Wrap(err, errVersionInput)
+			}
+			versionOut += "." + strconv.Itoa(version2)
+		} else {
+			versionOut += ".x"
 		}
-		versionOut += "." + strconv.Itoa(version2)
 	}
 	return &versionOut, nil
 }


Test patch:
diff --git a/pkg/controller/cache/replicationgroup/managed_test.go b/pkg/controller/cache/replicationgroup/managed_test.go
index 1137d4f56..e43a83a34 100644
--- a/pkg/controller/cache/replicationgroup/managed_test.go
+++ b/pkg/controller/cache/replicationgroup/managed_test.go
@@ -37,8 +37,9 @@ import (
 )
 
 const (
-	name                = "coolGroup"
-	engineVersionToTest = "5.0.2"
+	name                         = "coolGroup"
+	engineVersionToTest          = "5.0.2"
+	alternateEngineVersionToTest = "6.x"
 )
 
 var (
@@ -46,6 +47,7 @@ var (
 	autoFailoverEnabled      = true
 	cacheParameterGroupName  = "coolParamGroup"
 	engineVersion            = "5.0"
+	alternateEngineVersion   = "6.x"
 	port                     = 6379
 	host                     = "172.16.0.1"
 	maintenanceWindow        = "tomorrow"
@@ -789,6 +791,54 @@ func TestUpdate(t *testing.T) {
 			),
 			returnsErr: false,
 		},
+		{
+			name: "IncreaseReplicationsAndCheckBehaviourVersionx",
+			e: &external{client: &fake.MockClient{
+				MockDescribeReplicationGroups: func(ctx context.Context, _ *elasticache.DescribeReplicationGroupsInput, opts []func(*elasticache.Options)) (*elasticache.DescribeReplicationGroupsOutput, error) {
+					return &elasticache.DescribeReplicationGroupsOutput{
+						ReplicationGroups: []types.ReplicationGroup{{
+							Status:                 aws.String(v1beta1.StatusAvailable),
+							MemberClusters:         cacheClusters,
+							AutomaticFailover:      types.AutomaticFailoverStatusEnabled,
+							CacheNodeType:          aws.String(cacheNodeType),
+							SnapshotRetentionLimit: aws.Int32(int32(snapshotRetentionLimit)),
+							SnapshotWindow:         aws.String(snapshotWindow),
+							ClusterEnabled:         aws.Bool(true),
+							ConfigurationEndpoint:  &types.Endpoint{Address: aws.String(host), Port: int32(port)},
+						}},
+					}, nil
+				},
+				MockDescribeCacheClusters: func(ctx context.Context, _ *elasticache.DescribeCacheClustersInput, opts []func(*elasticache.Options)) (*elasticache.DescribeCacheClustersOutput, error) {
+					return &elasticache.DescribeCacheClustersOutput{
+						CacheClusters: []types.CacheCluster{
+							{EngineVersion: aws.String(engineVersion)},
+							{EngineVersion: aws.String(engineVersion)},
+							{EngineVersion: aws.String(engineVersion)},
+						},
+					}, nil
+				},
+				MockIncreaseReplicaCount: func(ctx context.Context, _ *elasticache.IncreaseReplicaCountInput, opts []func(*elasticache.Options)) (*elasticache.IncreaseReplicaCountOutput, error) {
+					return &elasticache.IncreaseReplicaCountOutput{}, nil
+				},
+			}},
+			r: replicationGroup(
+				withEngineVersion(alternateEngineVersionToTest),
+				withReplicationGroupID(name),
+				withProviderStatus(v1beta1.StatusAvailable),
+				withConditions(xpv1.Available()),
+				withMemberClusters(cacheClusters),
+				withNumCacheClusters(4),
+			),
+			want: replicationGroup(
+				withEngineVersion(alternateEngineVersion),
+				withReplicationGroupID(name),
+				withProviderStatus(v1beta1.StatusAvailable),
+				withConditions(xpv1.Available()),
+				withMemberClusters(cacheClusters),
+				withNumCacheClusters(4),
+			),
+			returnsErr: false,
+		},
 	}
 
 	for _, tc := range cases {