Skip to content

Commit 5b1314a

Browse files
Merge pull request #67 from nmvk/e2e-cme
Declarative Tests for ScalingUpAndDown and Async rollback fixes
2 parents e435358 + 7eba151 commit 5b1314a

File tree

15 files changed

+630
-63
lines changed

15 files changed

+630
-63
lines changed

pkg/resource/replication_group/annotations.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,14 @@ const (
2222
// LogDeliveryConfigurationRequest structs passed in as input to either the create or modify API called most
2323
// recently
2424
AnnotationLastRequestedLDCs = svcapitypes.AnnotationPrefix + "last-requested-log-delivery-configurations"
25+
// AnnotationLastRequestedCNT is an annotation whose value is passed in as input to either the create or modify API
26+
// called most recently
27+
AnnotationLastRequestedCNT = svcapitypes.AnnotationPrefix + "last-requested-cache-node-type"
28+
// AnnotationLastRequestedNNG is an annotation whose value is passed in as input to either the create or modify API
29+
// called most recently
30+
AnnotationLastRequestedNNG = svcapitypes.AnnotationPrefix + "last-requested-num-node-groups"
31+
// AnnotationLastRequestedNGC is an annotation whose value is the marshaled list of pointers to
32+
// NodeGroupConfiguration structs passed in as input to either the create or modify API called most
33+
// recently
34+
AnnotationLastRequestedNGC = svcapitypes.AnnotationPrefix + "last-requested-node-group-configuration"
2535
)

pkg/resource/replication_group/custom_set_output.go

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package replication_group
1616
import (
1717
"context"
1818
"encoding/json"
19+
"strconv"
1920

2021
svcapitypes "github.com/aws-controllers-k8s/elasticache-controller/apis/v1alpha1"
2122
ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1"
@@ -57,6 +58,8 @@ func (rm *resourceManager) CustomCreateReplicationGroupSetOutput(
5758
) (*svcapitypes.ReplicationGroup, error) {
5859
rm.customSetOutput(resp.ReplicationGroup, ko)
5960
rm.setAnnotationsFields(r, ko)
61+
rm.setLastRequestedNodeGroupConfiguration(r, ko)
62+
rm.setLastRequestedNumNodeGroups(r, ko)
6063
return ko, nil
6164
}
6265

@@ -76,6 +79,9 @@ func (rm *resourceManager) CustomModifyReplicationGroupSetOutput(
7679
}
7780
ko.Spec.LogDeliveryConfigurations = logDeliveryConfig
7881

82+
// Keep the value of desired for CacheNodeType.
83+
ko.Spec.CacheNodeType = r.ko.Spec.CacheNodeType
84+
7985
rm.setAnnotationsFields(r, ko)
8086
return ko, nil
8187
}
@@ -267,20 +273,36 @@ func (rm *resourceManager) provideEvents(
267273

268274
// setAnnotationsFields copies the desired object's annotations, populates any
269275
// relevant fields, and sets the latest object's annotations to this newly populated map.
276+
// Fields that are handled by custom modify implementation are not set here.
270277
// This should only be called upon a successful create or modify call.
271278
func (rm *resourceManager) setAnnotationsFields(
272279
r *resource,
273280
ko *svcapitypes.ReplicationGroup,
274281
) {
282+
annotations := getAnnotationsFields(r, ko)
283+
284+
rm.setLastRequestedLogDeliveryConfigurations(r, annotations)
285+
rm.setLastRequestedCacheNodeType(r, annotations)
286+
ko.ObjectMeta.Annotations = annotations
287+
}
288+
289+
// getAnnotationsFields return the annotations map that would be used to set the fields
290+
func getAnnotationsFields(
291+
r *resource,
292+
ko *svcapitypes.ReplicationGroup) map[string]string {
293+
294+
if ko.ObjectMeta.Annotations != nil {
295+
return ko.ObjectMeta.Annotations
296+
}
297+
275298
desiredAnnotations := r.ko.ObjectMeta.GetAnnotations()
276299
annotations := make(map[string]string)
277300
for k, v := range desiredAnnotations {
278301
annotations[k] = v
279302
}
280303

281-
rm.setLastRequestedLogDeliveryConfigurations(r, annotations)
282-
283304
ko.ObjectMeta.Annotations = annotations
305+
return annotations
284306
}
285307

286308
// setLastRequestedLogDeliveryConfigurations copies desired.Spec.LogDeliveryConfigurations
@@ -297,3 +319,43 @@ func (rm *resourceManager) setLastRequestedLogDeliveryConfigurations(
297319
annotations[AnnotationLastRequestedLDCs] = string(lastRequestedConfigs)
298320
}
299321
}
322+
323+
// setLastRequestedCacheNodeType copies desired.Spec.CacheNodeType into the annotation
324+
// of the object.
325+
func (rm *resourceManager) setLastRequestedCacheNodeType(
326+
r *resource,
327+
annotations map[string]string,
328+
) {
329+
if r.ko.Spec.CacheNodeType != nil {
330+
annotations[AnnotationLastRequestedCNT] = *r.ko.Spec.CacheNodeType
331+
}
332+
}
333+
334+
// setLastRequestedNodeGroupConfiguration copies desired.spec.NodeGroupConfiguration into the
335+
// annotation of the object
336+
func (rm *resourceManager) setLastRequestedNodeGroupConfiguration(
337+
r *resource,
338+
ko *svcapitypes.ReplicationGroup,
339+
) {
340+
annotations := getAnnotationsFields(r, ko)
341+
lastRequestedConfigs, err := json.Marshal(r.ko.Spec.NodeGroupConfiguration)
342+
if err != nil {
343+
annotations[AnnotationLastRequestedNGC] = "null"
344+
} else {
345+
annotations[AnnotationLastRequestedNGC] = string(lastRequestedConfigs)
346+
}
347+
}
348+
349+
// setLastRequestedNumNodeGroups copies desired.spec.NumNodeGroups into the
350+
// annotation of the object
351+
func (rm *resourceManager) setLastRequestedNumNodeGroups(
352+
r *resource,
353+
ko *svcapitypes.ReplicationGroup,
354+
) {
355+
annotations := getAnnotationsFields(r, ko)
356+
if r.ko.Spec.NumNodeGroups != nil {
357+
annotations[AnnotationLastRequestedNNG] = strconv.Itoa(int(*r.ko.Spec.NumNodeGroups))
358+
} else {
359+
annotations[AnnotationLastRequestedNNG] = "null"
360+
}
361+
}

pkg/resource/replication_group/custom_update_api.go

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ package replication_group
1515

1616
import (
1717
"context"
18+
"encoding/json"
1819
"fmt"
1920
"github.com/aws-controllers-k8s/runtime/pkg/requeue"
2021
"github.com/aws/aws-sdk-go/aws/awserr"
2122
"github.com/pkg/errors"
23+
"reflect"
2224
"sort"
25+
"strconv"
2326

2427
svcapitypes "github.com/aws-controllers-k8s/elasticache-controller/apis/v1alpha1"
2528
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
@@ -70,6 +73,28 @@ func (rm *resourceManager) CustomModifyReplicationGroup(
7073
requeue.DefaultRequeueAfterDuration)
7174
}
7275

76+
// Handle the asynchronous rollback case for while Scaling down.
77+
// This means that we have already attempted to apply the CacheNodeType once and
78+
// were not successful hence we will set a terminal condition.
79+
if !cacheNodeTypeRequiresUpdate(desired) && delta.DifferentAt("Spec.CacheNodeType") {
80+
return nil, awserr.New("InvalidParameterCombination", "Cannot update CacheNodeType, "+
81+
"Please refer to Events for more details", nil)
82+
83+
}
84+
85+
// Handle the asynchronous rollback for Resharding.
86+
if !nodeGroupRequiresUpdate(desired) && rm.shardConfigurationsDiffer(desired, latest) {
87+
88+
return nil, awserr.New("InvalidParameterCombination", "Cannot update NodeGroups, "+
89+
"Please refer to Events for more details", nil)
90+
}
91+
92+
// Handle NodeGroupConfiguration asynchronous rollback situations other than Resharding.
93+
if !nodeGroupRequiresUpdate(desired) && (rm.replicaCountDifference(desired, latest) != 0 && !delta.DifferentAt("Spec.ReplicasPerNodeGroup")) {
94+
return nil, awserr.New("InvalidParameterCombination", "Cannot update NodeGroupConfiguration, "+
95+
"Please refer to Events for more details", nil)
96+
}
97+
7398
// Order of operations when diffs map to multiple updates APIs:
7499
// 1. When automaticFailoverEnabled differs:
75100
// if automaticFailoverEnabled == false; do nothing in this custom logic, let the modify execute first.
@@ -316,7 +341,18 @@ func (rm *resourceManager) updateShardConfiguration(
316341
rm.log.V(1).Info("Error during ModifyReplicationGroupShardConfiguration", "error", respErr)
317342
return nil, respErr
318343
}
319-
return rm.setReplicationGroupOutput(desired, resp.ReplicationGroup)
344+
345+
r, err := rm.setReplicationGroupOutput(desired, resp.ReplicationGroup)
346+
347+
if err != nil {
348+
return r, err
349+
}
350+
351+
ko := r.ko.DeepCopy()
352+
// Update the annotations since API call was successful
353+
rm.setLastRequestedNodeGroupConfiguration(desired, ko)
354+
rm.setLastRequestedNumNodeGroups(desired, ko)
355+
return &resource{ko}, nil
320356
}
321357

322358
// newIncreaseReplicaCountRequestPayload returns an SDK-specific struct for the HTTP request
@@ -691,3 +727,45 @@ func (rm *resourceManager) newModifyReplicationGroupRequestPayload(
691727

692728
return input
693729
}
730+
731+
// cacheNodeTypeRequiresUpdate retrieves the last requested cacheNodeType saved in annotations and compares them
732+
// to the current desired cacheNodeType
733+
func cacheNodeTypeRequiresUpdate(desired *resource) bool {
734+
annotations := desired.ko.ObjectMeta.GetAnnotations()
735+
if val, ok := annotations[AnnotationLastRequestedCNT]; ok && desired.ko.Spec.CacheNodeType != nil {
736+
return val != *desired.ko.Spec.CacheNodeType
737+
}
738+
739+
// This means there is delta and no value in annotation or in Spec
740+
return true
741+
}
742+
743+
// nodeGroupRequiresUpdate retrieves the last applied NumNodeGroups and NodeGroupConfiguration and compares them
744+
// to the current desired NumNodeGroups and NodeGroupConfiguration
745+
func nodeGroupRequiresUpdate(desired *resource) bool {
746+
annotations := desired.ko.ObjectMeta.GetAnnotations()
747+
748+
if val, ok := annotations[AnnotationLastRequestedNNG]; ok && val != "null" {
749+
numNodes, err := strconv.ParseInt(val, 10, 64)
750+
751+
if err != nil {
752+
return false
753+
}
754+
755+
if numNodes != *desired.ko.Spec.NumNodeGroups {
756+
return true
757+
}
758+
759+
return false
760+
}
761+
762+
desiredNodeGroupConfig := desired.ko.Spec.NodeGroupConfiguration
763+
if val, ok := annotations[AnnotationLastRequestedNGC]; ok && val != "null" {
764+
var lastRequestedNodeGroupConfig []*svcapitypes.NodeGroupConfiguration
765+
_ = json.Unmarshal([]byte(val), &lastRequestedNodeGroupConfig)
766+
return !reflect.DeepEqual(desiredNodeGroupConfig, lastRequestedNodeGroupConfig)
767+
}
768+
769+
// This means there is delta and no value in annotation or in Spec
770+
return true
771+
}

0 commit comments

Comments
 (0)