Skip to content

Commit f6301ef

Browse files
authored
Add reservedConcurrentExecutions field for Function Resource (#12)
The AWS Lambda API support setting up reserved concurrent executions for a lambda functions. This patch adds a new field to allow users to set the concurrency configuration for functions. Description of changes: - Add `reservedConcurrentExecutions` fields for `Function` Resource - Add a new e2e test case to specifically test update and create operations on function resources with a non-nil `reservedConcurrentExecutions` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent b605c58 commit f6301ef

15 files changed

+180
-12
lines changed
+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
ack_generate_info:
2-
build_date: "2021-11-19T13:19:34Z"
2+
build_date: "2021-11-23T23:35:17Z"
33
build_hash: dca757058c55b80b4eb20f62f55829981a70f7de
44
go_version: go1.16.4
55
version: v0.15.2
6-
api_directory_checksum: 84d93710fb0b89a7ed99b0923611297242a55a99
6+
api_directory_checksum: a3c5e80eca3fc5591e8a0a2763d048f2ed4a6ddd
77
api_version: v1alpha1
88
aws_sdk_go_version: v1.40.28
99
generator_config_info:
10-
file_checksum: 4f4df395ccad50e23ed16cffcf1d4d24a909f9cc
10+
file_checksum: 47d63e3b8348d4f33111e6a3cbd6eca2412e9b46
1111
original_file_name: generator.yaml
1212
last_modification:
1313
reason: API generation

apis/v1alpha1/function.go

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apis/v1alpha1/generator.yaml

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ resources:
1212
Name:
1313
is_primary_key: true
1414
is_required: true
15-
# ReservedConcurrentExecutions:
16-
# from:
17-
# operation: PutFunctionConcurrency
18-
# path: ReservedConcurrentExecutions
15+
ReservedConcurrentExecutions:
16+
from:
17+
operation: PutFunctionConcurrency
18+
path: ReservedConcurrentExecutions
1919
Code:
2020
compare:
2121
is_ignored: true

apis/v1alpha1/zz_generated.deepcopy.go

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/lambda.services.k8s.aws_functions.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ spec:
137137
description: Set to true to publish the first version of the function
138138
during creation.
139139
type: boolean
140+
reservedConcurrentExecutions:
141+
description: The number of simultaneous executions to reserve for
142+
the function.
143+
format: int64
144+
type: integer
140145
role:
141146
description: The Amazon Resource Name (ARN) of the function's execution
142147
role.

generator.yaml

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ resources:
1212
Name:
1313
is_primary_key: true
1414
is_required: true
15-
# ReservedConcurrentExecutions:
16-
# from:
17-
# operation: PutFunctionConcurrency
18-
# path: ReservedConcurrentExecutions
15+
ReservedConcurrentExecutions:
16+
from:
17+
operation: PutFunctionConcurrency
18+
path: ReservedConcurrentExecutions
1919
Code:
2020
compare:
2121
is_ignored: true

helm/crds/lambda.services.k8s.aws_functions.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ spec:
137137
description: Set to true to publish the first version of the function
138138
during creation.
139139
type: boolean
140+
reservedConcurrentExecutions:
141+
description: The number of simultaneous executions to reserve for
142+
the function.
143+
format: int64
144+
type: integer
140145
role:
141146
description: The Amazon Resource Name (ARN) of the function's execution
142147
role.

pkg/resource/function/delta.go

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/resource/function/hooks.go

+62
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package function
1616
import (
1717
"context"
1818

19+
svcapitypes "github.com/aws-controllers-k8s/lambda-controller/apis/v1alpha1"
1920
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
2021
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
2122
"github.com/aws/aws-sdk-go/aws"
@@ -63,6 +64,12 @@ func (rm *resourceManager) customUpdateFunction(
6364
return nil, err
6465
}
6566
}
67+
if delta.DifferentAt("Spec.ReservedConcurrentExecutions") {
68+
err = rm.updateFunctionConcurrency(ctx, desired)
69+
if err != nil {
70+
return nil, err
71+
}
72+
}
6673

6774
readOneLatest, err := rm.ReadOne(ctx, desired)
6875
if err != nil {
@@ -346,3 +353,58 @@ func customPreCompare(
346353
}
347354
}
348355
}
356+
357+
// updateFunctionConcurrency calls UpdateFunctionConcurrency to update a specific
358+
// lambda function reserved concurrent executions.
359+
func (rm *resourceManager) updateFunctionConcurrency(
360+
ctx context.Context,
361+
desired *resource,
362+
) error {
363+
var err error
364+
rlog := ackrtlog.FromContext(ctx)
365+
exit := rlog.Trace("rm.updateFunctionConcurrency")
366+
defer exit(err)
367+
368+
dspec := desired.ko.Spec
369+
input := &svcsdk.PutFunctionConcurrencyInput{
370+
FunctionName: aws.String(*dspec.Name),
371+
}
372+
373+
if desired.ko.Spec.ReservedConcurrentExecutions != nil {
374+
input.ReservedConcurrentExecutions = aws.Int64(*desired.ko.Spec.ReservedConcurrentExecutions)
375+
} else {
376+
input.ReservedConcurrentExecutions = aws.Int64(0)
377+
}
378+
379+
_, err = rm.sdkapi.PutFunctionConcurrencyWithContext(ctx, input)
380+
rm.metrics.RecordAPICall("UPDATE", "PutFunctionConcurrency", err)
381+
if err != nil {
382+
return err
383+
}
384+
return nil
385+
}
386+
387+
// setResourceAdditionalFields will describe the fields that are not return by
388+
// GetFunctionConcurrency calls
389+
func (rm *resourceManager) setResourceAdditionalFields(
390+
ctx context.Context,
391+
ko *svcapitypes.Function,
392+
) (err error) {
393+
rlog := ackrtlog.FromContext(ctx)
394+
exit := rlog.Trace("rm.setResourceAdditionalFields")
395+
defer exit(err)
396+
397+
var getFunctionConcurrencyOutput *svcsdk.GetFunctionConcurrencyOutput
398+
getFunctionConcurrencyOutput, err = rm.sdkapi.GetFunctionConcurrencyWithContext(
399+
ctx,
400+
&svcsdk.GetFunctionConcurrencyInput{
401+
FunctionName: ko.Spec.Name,
402+
},
403+
)
404+
rm.metrics.RecordAPICall("GET", "GetFunctionConcurrency", err)
405+
if err != nil {
406+
return err
407+
}
408+
ko.Spec.ReservedConcurrentExecutions = getFunctionConcurrencyOutput.ReservedConcurrentExecutions
409+
return nil
410+
}

pkg/resource/function/sdk.go

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

templates/hooks/function/sdk_read_one_post_set_output.go.tpl

+3
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,7 @@
235235
ko.Spec.VPCConfig = f30
236236
} else {
237237
ko.Spec.VPCConfig = nil
238+
}
239+
if err := rm.setResourceAdditionalFields(ctx, ko); err != nil {
240+
return nil, err
238241
}

test/e2e/resources/function.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ spec:
1212
role: $LAMBDA_ROLE
1313
runtime: python3.9
1414
handler: main
15-
description: function created by ACK lambda-controller e2e tests
15+
description: function created by ACK lambda-controller e2e tests
16+
reservedConcurrentExecutions: $RESERVED_CONCURRENT_EXECUTIONS

test/e2e/tests/test_alias.py

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def lambda_function():
4747
replacements["BUCKET_NAME"] = resources.FunctionsBucketName
4848
replacements["LAMBDA_ROLE"] = resources.LambdaBasicRoleARN
4949
replacements["LAMBDA_FILE_NAME"] = resources.LambdaFunctionFileZip
50+
replacements["RESERVED_CONCURRENT_EXECUTIONS"] = "0"
5051
replacements["AWS_REGION"] = get_region()
5152

5253
# Load function CR

test/e2e/tests/test_event_source_mapping.py

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def lambda_function():
4747
replacements["BUCKET_NAME"] = resources.FunctionsBucketName
4848
replacements["LAMBDA_ROLE"] = resources.LambdaESMRoleARN
4949
replacements["LAMBDA_FILE_NAME"] = resources.LambdaFunctionFileZip
50+
replacements["RESERVED_CONCURRENT_EXECUTIONS"] = "0"
5051
replacements["AWS_REGION"] = get_region()
5152

5253
# Load function CR

test/e2e/tests/test_function.py

+73
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ def get_function(self, lambda_client, function_name: str) -> dict:
5252
logging.debug(e)
5353
return None
5454

55+
def get_function_concurrency(self, lambda_client, function_name: str) -> int:
56+
try:
57+
resp = lambda_client.get_function_concurrency(
58+
FunctionName=function_name
59+
)
60+
return resp['ReservedConcurrentExecutions']
61+
62+
except Exception as e:
63+
logging.debug(e)
64+
return None
65+
5566
def function_exists(self, lambda_client, function_name: str) -> bool:
5667
return self.get_function(lambda_client, function_name) is not None
5768

@@ -66,6 +77,7 @@ def test_smoke(self, lambda_client):
6677
replacements["BUCKET_NAME"] = resources.FunctionsBucketName
6778
replacements["LAMBDA_ROLE"] = resources.LambdaBasicRoleARN
6879
replacements["LAMBDA_FILE_NAME"] = resources.LambdaFunctionFileZip
80+
replacements["RESERVED_CONCURRENT_EXECUTIONS"] = "0"
6981
replacements["AWS_REGION"] = get_region()
7082

7183
# Load Lambda CR
@@ -121,3 +133,64 @@ def test_smoke(self, lambda_client):
121133
exists = self.function_exists(lambda_client, resource_name)
122134
assert not exists
123135

136+
def test_reserved_concurrent_executions(self, lambda_client):
137+
resource_name = random_suffix_name("lambda-function", 24)
138+
139+
resources = get_bootstrap_resources()
140+
logging.debug(resources)
141+
142+
replacements = REPLACEMENT_VALUES.copy()
143+
replacements["FUNCTION_NAME"] = resource_name
144+
replacements["BUCKET_NAME"] = resources.FunctionsBucketName
145+
replacements["LAMBDA_ROLE"] = resources.LambdaBasicRoleARN
146+
replacements["LAMBDA_FILE_NAME"] = resources.LambdaFunctionFileZip
147+
replacements["RESERVED_CONCURRENT_EXECUTIONS"] = "2"
148+
replacements["AWS_REGION"] = get_region()
149+
150+
# Load Lambda CR
151+
resource_data = load_lambda_resource(
152+
"function",
153+
additional_replacements=replacements,
154+
)
155+
logging.debug(resource_data)
156+
157+
# Create k8s resource
158+
ref = k8s.CustomResourceReference(
159+
CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL,
160+
resource_name, namespace="default",
161+
)
162+
k8s.create_custom_resource(ref, resource_data)
163+
cr = k8s.wait_resource_consumed_by_controller(ref)
164+
165+
assert cr is not None
166+
assert k8s.get_resource_exists(ref)
167+
168+
time.sleep(CREATE_WAIT_AFTER_SECONDS)
169+
170+
# Check Lambda function exists
171+
exists = self.function_exists(lambda_client, resource_name)
172+
assert exists
173+
174+
reservedConcurrentExecutions = self.get_function_concurrency(lambda_client, resource_name)
175+
assert reservedConcurrentExecutions == 2
176+
177+
# Update cr
178+
cr["spec"]["reservedConcurrentExecutions"] = 0
179+
180+
# Patch k8s resource
181+
k8s.patch_custom_resource(ref, cr)
182+
time.sleep(UPDATE_WAIT_AFTER_SECONDS)
183+
184+
# Check function updated fields
185+
reservedConcurrentExecutions = self.get_function_concurrency(lambda_client, resource_name)
186+
assert reservedConcurrentExecutions == 0
187+
188+
# Delete k8s resource
189+
_, deleted = k8s.delete_custom_resource(ref)
190+
assert deleted is True
191+
192+
time.sleep(DELETE_WAIT_AFTER_SECONDS)
193+
194+
# Check Lambda function doesn't exist
195+
exists = self.function_exists(lambda_client, resource_name)
196+
assert not exists

0 commit comments

Comments
 (0)