Skip to content

Commit 2c9ece5

Browse files
committed
Fix DBParameterGroup deadlock and add e2e test cases
Requeue after a create DBParameterGroup API call The DBParameterGroup API call doesn't allow users to set parameters, to set those users will have to call `ModifyDBParameterGroup`, however the delta function was failing to detect differences between two DBPGs mainly because `sdkCreate` makes a deep copy of the desired state and returns that to the main reconcilliation logic. This patch fixes this issue by requeueing right after a successfull create call. This allows the controller to properly read the latest state and compute the delta between latest and desired.
1 parent bb065a7 commit 2c9ece5

File tree

9 files changed

+76
-20
lines changed

9 files changed

+76
-20
lines changed
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
ack_generate_info:
2-
build_date: "2022-08-22T20:21:30Z"
3-
build_hash: fe61d04673fd4d9848d5f726b01e0689a16d3733
4-
go_version: go1.18.2
5-
version: v0.19.3-1-gfe61d04
2+
build_date: "2022-09-02T16:27:09Z"
3+
build_hash: cdb0d99c7c1d3d00e71623eb56e650960dcef0d5
4+
go_version: go1.18.3
5+
version: v0.19.3-2-gcdb0d99
66
api_directory_checksum: 83bde1f77a49ebd8acd36a1c5e231138b67aa883
77
api_version: v1alpha1
88
aws_sdk_go_version: v1.44.27
99
generator_config_info:
10-
file_checksum: 5d126ead4715f729c2fda976c422eb14a948f3da
10+
file_checksum: 868688590b240be6d557dd31f0583bad2d309cb8
1111
original_file_name: generator.yaml
1212
last_modification:
1313
reason: API generation

apis/v1alpha1/generator.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ resources:
319319
template_path: hooks/db_parameter_group/sdk_read_many_post_set_output.go.tpl
320320
delta_pre_compare:
321321
template_path: hooks/db_parameter_group/delta_pre_compare.go.tpl
322+
sdk_create_post_set_output:
323+
template_path: hooks/db_parameter_group/sdk_create_post_set_output.go.tpl
322324
fields:
323325
Name:
324326
is_primary_key: true

generator.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ resources:
319319
template_path: hooks/db_parameter_group/sdk_read_many_post_set_output.go.tpl
320320
delta_pre_compare:
321321
template_path: hooks/db_parameter_group/delta_pre_compare.go.tpl
322+
sdk_create_post_set_output:
323+
template_path: hooks/db_parameter_group/sdk_create_post_set_output.go.tpl
322324
fields:
323325
Name:
324326
is_primary_key: true

pkg/resource/db_parameter_group/hooks.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ import (
1717
"context"
1818
"fmt"
1919
"sync"
20+
"time"
2021

2122
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
2223
ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors"
24+
ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue"
2325
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
24-
25-
svcapitypes "github.com/aws-controllers-k8s/rds-controller/apis/v1alpha1"
2626
"github.com/aws/aws-sdk-go/aws"
2727
svcsdk "github.com/aws/aws-sdk-go/service/rds"
2828

29+
svcapitypes "github.com/aws-controllers-k8s/rds-controller/apis/v1alpha1"
2930
"github.com/aws-controllers-k8s/rds-controller/pkg/util"
3031
)
3132

@@ -36,8 +37,14 @@ const (
3637
)
3738

3839
var (
39-
errUnknownParameter = fmt.Errorf("unknown parameter")
40-
errUnmodifiableParameter = fmt.Errorf("parameter is not modifiable")
40+
errUnknownParameter = fmt.Errorf("unknown parameter")
41+
errUnmodifiableParameter = fmt.Errorf("parameter is not modifiable")
42+
errParamterGroupJustCreated = fmt.Errorf("parameter group just got created")
43+
44+
requeueWaitWhileCreating = ackrequeue.NeededAfter(
45+
errParamterGroupJustCreated,
46+
100*time.Millisecond,
47+
)
4148
)
4249

4350
func newErrUnknownParameter(name string) error {
@@ -277,7 +284,7 @@ func (rm *resourceManager) getParameters(
277284
err error,
278285
) {
279286
var marker *string
280-
287+
params = make(map[string]*string)
281288
for {
282289
resp, err := rm.sdkapi.DescribeDBParametersWithContext(
283290
ctx,
@@ -375,7 +382,7 @@ func (rm *resourceManager) modifyParameters(
375382
toModify map[string]*string,
376383
) (err error) {
377384
rlog := ackrtlog.FromContext(ctx)
378-
exit := rlog.Trace("rm.resetParameters")
385+
exit := rlog.Trace("rm.modifyParameters")
379386
defer func() { exit(err) }()
380387

381388
var pMeta *paramMeta
@@ -563,15 +570,17 @@ func (c *paramMetaCache) get(
563570
name string,
564571
fetcher metaFetcher,
565572
) (*paramMeta, error) {
566-
c.RLock()
567-
defer c.RUnlock()
568-
569573
var err error
570574
var found bool
571575
var metas map[string]paramMeta
572576
var meta paramMeta
573577

578+
// We need to release the lock right after the read operation, because
579+
// loadFamilly will might call a writeLock at L619
580+
c.RLock()
574581
metas, found = c.cache[family]
582+
c.RUnlock()
583+
575584
if !found {
576585
c.misses++
577586
metas, err = c.loadFamily(ctx, family, fetcher)

pkg/resource/db_parameter_group/sdk.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// We need to instantly requeue after a create operation, otherwise the controller
2+
// will override the latest resource with the desired resource overrideParameters
3+
// and cause the controller to not properly compare latest and desired resources.
4+
return &resource{ko}, requeueWaitWhileCreating

test/e2e/db_parameter_group.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ def get(db_parameter_group_name):
3232
except c.exceptions.DBParameterGroupNotFoundFault:
3333
return None
3434

35+
def get_parameters(db_parameter_group_name):
36+
"""Returns a dict containing the paramters of a given parameter group
37+
38+
If no such DB parameter group exists, returns None.
39+
"""
40+
c = boto3.client('rds')
41+
try:
42+
resp = c.describe_db_parameters(
43+
DBParameterGroupName=db_parameter_group_name,
44+
)
45+
return resp['Parameters']
46+
except c.exceptions.DBParameterGroupNotFoundFault:
47+
return None
3548

3649
def get_tags(db_parameter_group_arn):
3750
"""Returns a dict containing the DB parameter group's tag records from the

test/e2e/resources/db_parameter_group_postgres13_standard.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ spec:
66
name: $DB_PARAMETER_GROUP_NAME
77
description: $DB_PARAMETER_GROUP_DESC
88
family: postgres13
9+
parameterOverrides:
10+
array_nulls: "1"
11+
authentication_timeout: "50"
912
tags:
1013
- key: environment
1114
value: dev

test/e2e/tests/test_db_parameter_group.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,9 @@
2828

2929
RESOURCE_PLURAL = 'dbparametergroups'
3030

31+
CREATE_WAIT_AFTER_SECONDS = 10
32+
MODIFY_WAIT_AFTER_SECONDS = 10
3133
DELETE_WAIT_AFTER_SECONDS = 10
32-
# NOTE(jaypipes): According to the RDS API documentation, updating tags can
33-
# take several minutes before the new tag values are available due to caching.
34-
MODIFY_WAIT_AFTER_SECONDS = 180
35-
3634

3735
@service_marker
3836
@pytest.mark.canary
@@ -57,17 +55,25 @@ def test_create_delete_postgres13_standard(self):
5755
resource_name, namespace="default",
5856
)
5957
k8s.create_custom_resource(ref, resource_data)
58+
time.sleep(CREATE_WAIT_AFTER_SECONDS)
6059
cr = k8s.wait_resource_consumed_by_controller(ref)
6160

6261
assert cr is not None
6362
assert k8s.get_resource_exists(ref)
64-
condition.assert_synced(ref)
6563

6664
# Let's check that the DB parameter group appears in RDS
6765
latest = db_parameter_group.get(resource_name)
6866
assert latest is not None
6967
assert latest['Description'] == resource_desc
7068

69+
params = db_parameter_group.get_parameters(resource_name)
70+
test_params = list(filter(lambda x: x["ParameterName"] in ["array_nulls", "authentication_timeout"], params))
71+
assert len(test_params) == 2
72+
assert test_params[0]["ParameterName"] == "array_nulls"
73+
assert test_params[0]["ParameterValue"] == "1"
74+
assert test_params[1]["ParameterName"] == "authentication_timeout"
75+
assert test_params[1]["ParameterValue"] == "50"
76+
7177
arn = latest['DBParameterGroupArn']
7278
expect_tags = [
7379
{"Key": "environment", "Value": "dev"}
@@ -83,8 +89,16 @@ def test_create_delete_postgres13_standard(self):
8389
"value": "prod",
8490
}
8591
]
92+
new_params = {
93+
"array_nulls": "1",
94+
"authentication_timeout": "60",
95+
}
96+
8697
updates = {
87-
"spec": {"tags": new_tags},
98+
"spec": {
99+
"tags": new_tags,
100+
"parameterOverrides": new_params,
101+
},
88102
}
89103
k8s.patch_custom_resource(ref, updates)
90104
time.sleep(MODIFY_WAIT_AFTER_SECONDS)
@@ -97,6 +111,11 @@ def test_create_delete_postgres13_standard(self):
97111
}
98112
]
99113
assert latest_tags == after_update_expected_tags
114+
params = db_parameter_group.get_parameters(resource_name)
115+
test_params = list(filter(lambda x: x["ParameterName"] in ["array_nulls", "authentication_timeout"], params))
116+
assert len(test_params) == 2
117+
assert test_params[1]["ParameterName"] == "authentication_timeout"
118+
assert test_params[1]["ParameterValue"] == "60"
100119

101120
# Delete the k8s resource on teardown of the module
102121
k8s.delete_custom_resource(ref)

0 commit comments

Comments
 (0)