Skip to content

Commit ba49b9f

Browse files
authored
Function version provisioned concurrency (#116)
Issue #, if available: Description of changes: By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 9fd8470 commit ba49b9f

File tree

13 files changed

+261
-9
lines changed

13 files changed

+261
-9
lines changed
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
ack_generate_info:
2-
build_date: "2024-02-05T21:49:22Z"
2+
build_date: "2024-02-05T22:05:18Z"
33
build_hash: 5b4565ec2712d29988b8123aeeed6a4af57467bf
44
go_version: go1.21.6
55
version: v0.29.2-4-g5b4565e
6-
api_directory_checksum: ade63fbeb51f31fc6deec256bb90966c67e73825
6+
api_directory_checksum: c67645b15db39980ba51ff6303c34c5aafc55a9e
77
api_version: v1alpha1
88
aws_sdk_go_version: v1.44.181
99
generator_config_info:
10-
file_checksum: ad8dfe3985ccc54cbe75614e8bf1381b9212bfee
10+
file_checksum: d71df9c47727f5905a2be2ac8d5cfceff3c7bbec
1111
original_file_name: generator.yaml
1212
last_modification:
1313
reason: API generation

apis/v1alpha1/generator.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ resources:
230230
from:
231231
operation: PutFunctionEventInvokeConfig
232232
path: .
233+
ProvisionedConcurrencyConfig:
234+
from:
235+
operation: PutProvisionedConcurrencyConfig
236+
path: .
233237
tags:
234238
ignore: true
235239
update_operation:

apis/v1alpha1/version.go

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apis/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
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_versions.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ spec:
117117
type: string
118118
type: object
119119
type: object
120+
provisionedConcurrencyConfig:
121+
properties:
122+
functionName:
123+
type: string
124+
provisionedConcurrentExecutions:
125+
format: int64
126+
type: integer
127+
qualifier:
128+
type: string
129+
type: object
120130
revisionID:
121131
description: |-
122132
Only update the function if the revision ID matches the ID that's specified.

generator.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ resources:
230230
from:
231231
operation: PutFunctionEventInvokeConfig
232232
path: .
233+
ProvisionedConcurrencyConfig:
234+
from:
235+
operation: PutProvisionedConcurrencyConfig
236+
path: .
233237
tags:
234238
ignore: true
235239
update_operation:

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ spec:
117117
type: string
118118
type: object
119119
type: object
120+
provisionedConcurrencyConfig:
121+
properties:
122+
functionName:
123+
type: string
124+
provisionedConcurrentExecutions:
125+
format: int64
126+
type: integer
127+
qualifier:
128+
type: string
129+
type: object
120130
revisionID:
121131
description: |-
122132
Only update the function if the revision ID matches the ID that's specified.

pkg/resource/version/delta.go

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/resource/version/hooks.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ func (rm *resourceManager) customUpdateVersion(
7171
}
7272
}
7373

74+
if delta.DifferentAt("Spec.ProvisionedConcurrencyConfig") {
75+
err = rm.updateProvisionedConcurrency(ctx, desired)
76+
if err != nil {
77+
return nil, err
78+
}
79+
}
80+
7481
readOneLatest, err := rm.ReadOne(ctx, desired)
7582
if err != nil {
7683
return nil, err
@@ -193,6 +200,87 @@ func (rm *resourceManager) setFunctionEventInvokeConfig(
193200
return nil
194201
}
195202

203+
// updateProvisionedConcurrency calls `PutProvisionedConcurrencyConfig` to update the fields
204+
// or `DeleteProvisionedConcurrencyConfig` if users removes the fields
205+
func (rm *resourceManager) updateProvisionedConcurrency(
206+
ctx context.Context,
207+
desired *resource,
208+
) error {
209+
var err error
210+
rlog := ackrtlog.FromContext(ctx)
211+
exit := rlog.Trace("rm.updateProvisionedConcurrency")
212+
defer exit(err)
213+
214+
if desired.ko.Status.Version == nil {
215+
return nil
216+
}
217+
218+
// Check if the user deleted the 'ProvisionedConcurrency' configuration
219+
// If yes, delete ProvisionedConcurrencyConfig
220+
if desired.ko.Spec.ProvisionedConcurrencyConfig == nil || desired.ko.Spec.ProvisionedConcurrencyConfig.ProvisionedConcurrentExecutions == nil {
221+
input_delete := &svcsdk.DeleteProvisionedConcurrencyConfigInput{
222+
FunctionName: aws.String(*desired.ko.Spec.FunctionName),
223+
Qualifier: aws.String(*desired.ko.Status.Version),
224+
}
225+
_, err = rm.sdkapi.DeleteProvisionedConcurrencyConfigWithContext(ctx, input_delete)
226+
rm.metrics.RecordAPICall("DELETE", "DeleteProvisionedConcurrency", err)
227+
if err != nil {
228+
return err
229+
}
230+
return nil
231+
}
232+
233+
dspec := desired.ko.Spec
234+
input := &svcsdk.PutProvisionedConcurrencyConfigInput{
235+
FunctionName: aws.String(*desired.ko.Spec.FunctionName),
236+
Qualifier: aws.String(*desired.ko.Status.Version),
237+
ProvisionedConcurrentExecutions: aws.Int64(*dspec.ProvisionedConcurrencyConfig.ProvisionedConcurrentExecutions),
238+
}
239+
240+
_, err = rm.sdkapi.PutProvisionedConcurrencyConfigWithContext(ctx, input)
241+
rm.metrics.RecordAPICall("UPDATE", "UpdateProvisionedConcurrency", err)
242+
if err != nil {
243+
return err
244+
}
245+
return nil
246+
}
247+
248+
// setProvisionedConcurrencyConfig sets the Provisioned Concurrency
249+
// for the Function's Version
250+
func (rm *resourceManager) setProvisionedConcurrencyConfig(
251+
ctx context.Context,
252+
ko *svcapitypes.Version,
253+
) (err error) {
254+
rlog := ackrtlog.FromContext(ctx)
255+
exit := rlog.Trace("rm.setProvisionedConcurrencyConfig")
256+
defer exit(err)
257+
258+
var getProvisionedConcurrencyConfigOutput *svcsdk.GetProvisionedConcurrencyConfigOutput
259+
getProvisionedConcurrencyConfigOutput, err = rm.sdkapi.GetProvisionedConcurrencyConfigWithContext(
260+
ctx,
261+
&svcsdk.GetProvisionedConcurrencyConfigInput{
262+
FunctionName: ko.Spec.FunctionName,
263+
Qualifier: ko.Status.Version,
264+
},
265+
)
266+
rm.metrics.RecordAPICall("GET", "GetProvisionedConcurrencyConfig", err)
267+
268+
if err != nil {
269+
if awserr, ok := ackerr.AWSError(err); ok && (awserr.Code() == "ProvisionedConcurrencyConfigNotFoundException" || awserr.Code() == "ResourceNotFoundException") {
270+
ko.Spec.ProvisionedConcurrencyConfig = nil
271+
} else {
272+
return err
273+
}
274+
} else {
275+
// creating ProvisionedConcurrency object to store the values returned from `Get` call
276+
cloudProvisionedConcurrency := &svcapitypes.PutProvisionedConcurrencyConfigInput{}
277+
cloudProvisionedConcurrency.ProvisionedConcurrentExecutions = getProvisionedConcurrencyConfigOutput.RequestedProvisionedConcurrentExecutions
278+
ko.Spec.ProvisionedConcurrencyConfig = cloudProvisionedConcurrency
279+
}
280+
281+
return nil
282+
}
283+
196284
// setResourceAdditionalFields will describe the fields that are not return by the
197285
// getFunctionConfiguration API call
198286
func (rm *resourceManager) setResourceAdditionalFields(
@@ -209,5 +297,11 @@ func (rm *resourceManager) setResourceAdditionalFields(
209297
return err
210298
}
211299

300+
// To set Provisioned Concurrency for the function's version
301+
err = rm.setProvisionedConcurrencyConfig(ctx, ko)
302+
if err != nil {
303+
return err
304+
}
305+
212306
return nil
213307
}

pkg/resource/version/sdk.go

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

templates/hooks/version/sdk_create_post_set_output.go.tpl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,10 @@ if ko.Spec.FunctionEventInvokeConfig != nil {
33
if err != nil{
44
return nil, err
55
}
6+
}
7+
if ko.Spec.ProvisionedConcurrencyConfig != nil {
8+
err = rm.updateProvisionedConcurrency(ctx,desired)
9+
if err != nil{
10+
return nil, err
11+
}
612
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: lambda.services.k8s.aws/v1alpha1
2+
kind: Version
3+
metadata:
4+
name: $VERSION_NAME
5+
annotations:
6+
services.k8s.aws/region: $AWS_REGION
7+
spec:
8+
functionName: $FUNCTION_NAME
9+
provisionedConcurrencyConfig:
10+
provisionedConcurrentExecutions: $PROVISIONED_CONCURRENT_EXECUTIONS
11+
description: version created by ACK lambda-controller e2e tests

test/e2e/tests/test_version.py

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def test_smoke(self, lambda_client, lambda_function):
9191
replacements["AWS_REGION"] = get_region()
9292
replacements["FUNCTION_NAME"] = lambda_function_name
9393
replacements["VERSION_NAME"] = resource_name
94-
94+
9595
# Load Lambda CR
9696
resource_data = load_lambda_resource(
9797
"version",
@@ -280,7 +280,7 @@ def test_smoke_ref(self, lambda_client, lambda_function):
280280

281281
# Check alias doesn't exist
282282
assert not lambda_validator.version_exists(function_resource_name, version_number)
283-
283+
284284
def test_function_event_invoke_config(self, lambda_client, lambda_function):
285285
(_, function_resource) = lambda_function
286286
lambda_function_name = function_resource["spec"]["name"]
@@ -325,7 +325,7 @@ def test_function_event_invoke_config(self, lambda_client, lambda_function):
325325

326326
version_number = cr['status']['version']
327327

328-
# Check version exists
328+
# Check version exists
329329
assert lambda_validator.version_exists(lambda_function_name, version_number)
330330

331331
# Update cr
@@ -359,4 +359,80 @@ def test_function_event_invoke_config(self, lambda_client, lambda_function):
359359
time.sleep(DELETE_WAIT_AFTER_SECONDS)
360360

361361
# Check version doesn't exist
362-
assert not lambda_validator.version_exists(lambda_function_name, version_number)
362+
assert not lambda_validator.version_exists(lambda_function_name, version_number)
363+
364+
def test_provisioned_concurrency_config(self, lambda_client, lambda_function):
365+
(_, function_resource) = lambda_function
366+
lambda_function_name = function_resource["spec"]["name"]
367+
368+
resource_name = random_suffix_name("lambda-version", 24)
369+
370+
resources = get_bootstrap_resources()
371+
logging.debug(resources)
372+
373+
replacements = REPLACEMENT_VALUES.copy()
374+
replacements["AWS_REGION"] = get_region()
375+
replacements["FUNCTION_NAME"] = lambda_function_name
376+
replacements["VERSION_NAME"] = resource_name
377+
replacements["PROVISIONED_CONCURRENT_EXECUTIONS"] = "1"
378+
379+
# Load version CR
380+
resource_data = load_lambda_resource(
381+
"version_provisioned_concurrency",
382+
additional_replacements=replacements,
383+
)
384+
logging.debug(resource_data)
385+
386+
# Create k8s resource
387+
ref = k8s.CustomResourceReference(
388+
CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL,
389+
resource_name, namespace="default",
390+
)
391+
k8s.create_custom_resource(ref, resource_data)
392+
cr = k8s.wait_resource_consumed_by_controller(ref)
393+
394+
assert cr is not None
395+
assert k8s.get_resource_exists(ref)
396+
397+
time.sleep(CREATE_WAIT_AFTER_SECONDS)
398+
399+
cr = k8s.wait_resource_consumed_by_controller(ref)
400+
401+
lambda_validator = LambdaValidator(lambda_client)
402+
403+
version_number = cr['status']['version']
404+
405+
# Check version exists
406+
assert lambda_validator.version_exists(lambda_function_name, version_number)
407+
408+
# Update provisioned_concurrency
409+
cr["spec"]["provisionedConcurrencyConfig"]["provisionedConcurrentExecutions"] = 2
410+
411+
# Patch k8s resource
412+
k8s.patch_custom_resource(ref, cr)
413+
time.sleep(UPDATE_WAIT_AFTER_SECONDS)
414+
415+
#Check provisioned_concurrency_config update fields
416+
provisioned_concurrency_config = lambda_validator.get_provisioned_concurrency_config(lambda_function_name, version_number)
417+
assert provisioned_concurrency_config["RequestedProvisionedConcurrentExecutions"] == 2
418+
419+
# Delete provisioned_concurrency from version
420+
cr = k8s.wait_resource_consumed_by_controller(ref)
421+
cr["spec"]["provisionedConcurrencyConfig"] = None
422+
423+
# Patch k8s resource
424+
k8s.patch_custom_resource(ref, cr)
425+
time.sleep(UPDATE_WAIT_AFTER_SECONDS)
426+
427+
#Check provisioned_concurrency_config is deleted
428+
assert not lambda_validator.get_provisioned_concurrency_config(lambda_function_name, version_number)
429+
430+
# Delete k8s resource
431+
_, deleted = k8s.delete_custom_resource(ref)
432+
assert deleted
433+
434+
time.sleep(DELETE_WAIT_AFTER_SECONDS)
435+
436+
# Check version doesn't exist
437+
assert not lambda_validator.version_exists(lambda_function_name, version_number)
438+

0 commit comments

Comments
 (0)