diff --git a/pkg/clients/elasticache/elasticache.go b/pkg/clients/elasticache/elasticache.go index b1466460d0..89101573d3 100644 --- a/pkg/clients/elasticache/elasticache.go +++ b/pkg/clients/elasticache/elasticache.go @@ -569,7 +569,7 @@ func generateReplicationGroupPendingModifiedValues(in elasticachetypes.Replicati func newEndpoint(rg elasticachetypes.ReplicationGroup) v1beta1.Endpoint { var e *elasticachetypes.Endpoint switch { - case !aws.ToBool(rg.ClusterEnabled) && len(rg.NodeGroups) > 0: + case !aws.ToBool(rg.ClusterEnabled) && len(rg.NodeGroups) > 0 && rg.NodeGroups[0].PrimaryEndpoint != nil: e = rg.NodeGroups[0].PrimaryEndpoint case aws.ToBool(rg.ClusterEnabled) && rg.ConfigurationEndpoint != nil: e = rg.ConfigurationEndpoint diff --git a/pkg/controller/cache/replicationgroup/managed_test.go b/pkg/controller/cache/replicationgroup/managed_test.go index a291bc422b..e7309b332a 100644 --- a/pkg/controller/cache/replicationgroup/managed_test.go +++ b/pkg/controller/cache/replicationgroup/managed_test.go @@ -19,6 +19,7 @@ package replicationgroup import ( "context" "testing" + "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/elasticache" @@ -76,6 +77,12 @@ type testCase struct { type replicationGroupModifier func(*v1beta1.ReplicationGroup) +func withAutomaticFailover(v types.AutomaticFailoverStatus) replicationGroupModifier { + return func(r *v1beta1.ReplicationGroup) { + r.Status.AtProvider.AutomaticFailover = string(v) + } +} + func withConditions(c ...xpv1.Condition) replicationGroupModifier { return func(r *v1beta1.ReplicationGroup) { r.Status.ConditionedStatus.Conditions = c } } @@ -84,6 +91,10 @@ func withProviderStatus(s string) replicationGroupModifier { return func(r *v1beta1.ReplicationGroup) { r.Status.AtProvider.Status = s } } +func withProviderStatusNodeGroups(n []v1beta1.NodeGroup) replicationGroupModifier { + return func(r *v1beta1.ReplicationGroup) { r.Status.AtProvider.NodeGroups = n } +} + func withReplicationGroupID(n string) replicationGroupModifier { return func(r *v1beta1.ReplicationGroup) { meta.SetExternalName(r, n) } } @@ -122,6 +133,10 @@ func withNumNodeGroups(n int) replicationGroupModifier { return func(r *v1beta1.ReplicationGroup) { r.Spec.ForProvider.NumNodeGroups = &n } } +func withAtRestEncryptionEnabled(b bool) replicationGroupModifier { + return func(r *v1beta1.ReplicationGroup) { r.Spec.ForProvider.AtRestEncryptionEnabled = &b } +} + func withNumCacheClusters(n int) replicationGroupModifier { return func(r *v1beta1.ReplicationGroup) { r.Spec.ForProvider.NumCacheClusters = &n } } @@ -205,6 +220,30 @@ func TestCreate(t *testing.T) { } func TestObserve(t *testing.T) { + var makeStringPtr = func(id string) *string { + var p = new(string) + *p = id + return p + } + var makeBoolPtr = func(v bool) *bool { + var p = new(bool) + *p = v + return p + } + var makeTimePtr = func(t time.Time) *time.Time { + var p = new(time.Time) + *p = t + return p + } + var makeArn = func(id string) *string { + return makeStringPtr("arn:aws:elasticache:eu-central-1:1001001ESOES:replicationgroup:" + id) + } + var makeDescription = func(id string) *string { + return makeStringPtr("Redis Group:" + id) + } + + var successfulObserveAfterCreationFailed = "SuccessfulObserveAfterCreationFailed" + cases := []testCase{ { name: "SuccessfulObserveWhileGroupCreating", @@ -294,6 +333,62 @@ func TestObserve(t *testing.T) { ), tokenCreated: true, }, + { + name: successfulObserveAfterCreationFailed, // Replicates issue #1838 + 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{{ + ARN: makeArn(successfulObserveAfterCreationFailed), + AtRestEncryptionEnabled: makeBoolPtr(true), + AuthTokenEnabled: makeBoolPtr(true), + AutomaticFailover: types.AutomaticFailoverStatusEnabled, + AuthTokenLastModifiedDate: makeTimePtr(time.Date(2023, 6, 15, 12, 21, 05, 0, time.UTC)), + DataTiering: types.DataTieringStatusDisabled, + Description: makeDescription(successfulObserveAfterCreationFailed), + MultiAZ: types.MultiAZStatusDisabled, + NodeGroups: []types.NodeGroup{ + {NodeGroupId: aws.String("0001"), Status: makeStringPtr(v1beta1.StatusCreateFailed)}, + {NodeGroupId: aws.String("0002"), Status: makeStringPtr(v1beta1.StatusCreateFailed)}, + }, + ReplicationGroupCreateTime: makeTimePtr(time.Date(2023, 6, 15, 12, 21, 05, 0, time.UTC)), + Status: aws.String(v1beta1.StatusCreateFailed), + TransitEncryptionEnabled: makeBoolPtr(true), + }}, + }, nil + }, + MockListTagsForResource: func(ctx context.Context, _ *elasticache.ListTagsForResourceInput, opts []func(*elasticache.Options)) (*elasticache.ListTagsForResourceOutput, error) { + return &elasticache.ListTagsForResourceOutput{ + TagList: []types.Tag{ + {Key: aws.String("key1"), Value: aws.String("val1")}, + {Key: aws.String("key2"), Value: aws.String("val2")}, + }, + }, nil + }, + }}, + r: replicationGroup( + withReplicationGroupID(name), + withConditions(xpv1.Creating()), + withClusterEnabled(true), + withNumNodeGroups(2), + withAtRestEncryptionEnabled(true), + withAuthEnabled(true), + ), + want: replicationGroup( + withReplicationGroupID(name), + withProviderStatus(v1beta1.StatusCreateFailed), + withConditions(xpv1.Unavailable()), + withNumNodeGroups(2), + withAutomaticFailover(types.AutomaticFailoverStatusEnabled), + withProviderStatusNodeGroups([]v1beta1.NodeGroup{ + {NodeGroupID: "0001", Status: v1beta1.StatusCreateFailed}, + {NodeGroupID: "0002", Status: v1beta1.StatusCreateFailed}, + }), + withAtRestEncryptionEnabled(true), + withAuthEnabled(true), + ), + tokenCreated: false, + }, { name: "SuccessfulObserveLateInitialized", e: &external{