Skip to content

Commit 80f3397

Browse files
authored
Support functions CodeSigningConfig updates (#15)
Issue #, if available: This patch essentially adds support for updating `CodeSigningConfig` for lambda functions. This patch also fixes a bug where the controller tries to update a function that is in a Pending state - the fix is done by re-queuing whenever a `Status.State` is 'Pending'. Along the way, this this patch also fixes few existing issues with the current e2e tests. Description of changes: - Requeue whenever `Function.Status.State` is `Pending` - Do not try to update function code if only `code.s3Key` is modified - Add a hook to prevent the create code path from trying to delete the code signing config when it's given as an empty string - Fixes a bug where created dynamodb tables were not deleted - Fixes a bug where the cr used in the e2e tests were not completely up to date and need to be refetched By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 5110df4 commit 80f3397

File tree

13 files changed

+276
-18
lines changed

13 files changed

+276
-18
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: "2021-11-23T23:35:17Z"
3-
build_hash: dca757058c55b80b4eb20f62f55829981a70f7de
2+
build_date: "2021-12-20T13:43:11Z"
3+
build_hash: 6f17f51682dc0d16c36aa456fd22855ce9282fbc
44
go_version: go1.16.4
55
version: v0.15.2
66
api_directory_checksum: a3c5e80eca3fc5591e8a0a2763d048f2ed4a6ddd
77
api_version: v1alpha1
88
aws_sdk_go_version: v1.40.28
99
generator_config_info:
10-
file_checksum: 47d63e3b8348d4f33111e6a3cbd6eca2412e9b46
10+
file_checksum: f04b298afa1fd7fd3980226d52412de9ca1523d4
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
@@ -35,6 +35,8 @@ resources:
3535
code: customPreCompare(delta, a, b)
3636
sdk_read_one_post_set_output:
3737
template_path: hooks/function/sdk_read_one_post_set_output.go.tpl
38+
sdk_create_post_build_request:
39+
template_path: hooks/function/sdk_create_post_build_request.go.tpl
3840
update_operation:
3941
custom_method_name: customUpdateFunction
4042
Alias:

generator.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ resources:
3535
code: customPreCompare(delta, a, b)
3636
sdk_read_one_post_set_output:
3737
template_path: hooks/function/sdk_read_one_post_set_output.go.tpl
38+
sdk_create_post_build_request:
39+
template_path: hooks/function/sdk_create_post_build_request.go.tpl
3840
update_operation:
3941
custom_method_name: customUpdateFunction
4042
Alias:

helm/templates/deployment.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,12 @@ spec:
7575
value: {{ join "," .Values.resourceTags | quote }}
7676
terminationGracePeriodSeconds: 10
7777
nodeSelector: {{ toYaml .Values.deployment.nodeSelector | nindent 8 }}
78+
{{ if .Values.deployment.tolerations -}}
79+
tolerations: {{ toYaml .Values.deployment.tolerations | nindent 8 }}
80+
{{ end -}}
81+
{{ if .Values.deployment.affinity -}}
82+
affinity: {{ toYaml .Values.deployment.affinity | nindent 8 }}
83+
{{ end -}}
84+
{{ if .Values.deployment.priorityClassName -}}
85+
priorityClassName: {{ .Values.deployment.priorityClassName -}}
86+
{{ end -}}

helm/values.yaml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,20 @@ deployment:
1515
annotations: {}
1616
labels: {}
1717
containerPort: 8080
18+
# Which nodeSelector to set?
19+
# See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector
1820
nodeSelector:
1921
kubernetes.io/os: linux
20-
22+
# Which tolerations to set?
23+
# See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/
24+
tolerations: {}
25+
# What affinity to set?
26+
# See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity
27+
affinity: {}
28+
# Which priorityClassName to set?
29+
# See: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#pod-priority
30+
priorityClassName:
31+
2132
metrics:
2233
service:
2334
# Set to true to automatically create a Kubernetes Service resource for the

pkg/resource/function/hooks.go

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,38 @@ package function
1515

1616
import (
1717
"context"
18+
"errors"
19+
"time"
1820

1921
svcapitypes "github.com/aws-controllers-k8s/lambda-controller/apis/v1alpha1"
2022
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
23+
ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue"
2124
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
2225
"github.com/aws/aws-sdk-go/aws"
2326
svcsdk "github.com/aws/aws-sdk-go/service/lambda"
2427
)
2528

29+
var (
30+
ErrFunctionPending = errors.New("Function in 'Pending' state, cannot be modified or deleted")
31+
)
32+
33+
var (
34+
requeueWaitWhilePending = ackrequeue.NeededAfter(
35+
ErrFunctionPending,
36+
5*time.Second,
37+
)
38+
)
39+
40+
// isFunctionPending returns true if the supplied Lambda Function is in a pending
41+
// state
42+
func isFunctionPending(r *resource) bool {
43+
if r.ko.Status.State == nil {
44+
return false
45+
}
46+
state := *r.ko.Status.State
47+
return state == string(svcapitypes.State_Pending)
48+
}
49+
2650
// customUpdateFunction patches each of the resource properties in the backend AWS
2751
// service API and returns a new resource with updated fields.
2852
func (rm *resourceManager) customUpdateFunction(
@@ -36,8 +60,12 @@ func (rm *resourceManager) customUpdateFunction(
3660
exit := rlog.Trace("rm.customUpdateFunction")
3761
defer exit(err)
3862

63+
if isFunctionPending(desired) {
64+
return nil, requeueWaitWhilePending
65+
}
66+
3967
if delta.DifferentAt("Spec.Code") {
40-
err = rm.updateFunctionCode(ctx, desired)
68+
err = rm.updateFunctionCode(ctx, desired, delta)
4169
if err != nil {
4270
return nil, err
4371
}
@@ -70,6 +98,12 @@ func (rm *resourceManager) customUpdateFunction(
7098
return nil, err
7199
}
72100
}
101+
if delta.DifferentAt("Spec.CodeSigningConfigARN") {
102+
err = rm.updateFunctionCodeSigningConfig(ctx, desired)
103+
if err != nil {
104+
return nil, err
105+
}
106+
}
73107

74108
readOneLatest, err := rm.ReadOne(ctx, desired)
75109
if err != nil {
@@ -247,12 +281,22 @@ func (rm *resourceManager) updateFunctionTags(
247281
func (rm *resourceManager) updateFunctionCode(
248282
ctx context.Context,
249283
desired *resource,
284+
delta *ackcompare.Delta,
250285
) error {
251286
var err error
252287
rlog := ackrtlog.FromContext(ctx)
253288
exit := rlog.Trace("rm.updateFunctionCode")
254289
defer exit(err)
255290

291+
if delta.DifferentAt("Spec.Code.S3Key") &&
292+
!delta.DifferentAt("Spec.Code.S3Bucket") &&
293+
!delta.DifferentAt("Spec.Code.S3ObjectVersion") &&
294+
!delta.DifferentAt("Spec.Code.ImageURI") {
295+
log := ackrtlog.FromContext(ctx)
296+
log.Info("updating code.s3Key field is not currently supported.")
297+
return nil
298+
}
299+
256300
dspec := desired.ko.Spec
257301
input := &svcsdk.UpdateFunctionCodeInput{
258302
FunctionName: aws.String(*dspec.Name),
@@ -265,10 +309,6 @@ func (rm *resourceManager) updateFunctionCode(
265309
case dspec.Code.S3Bucket != nil,
266310
dspec.Code.S3Key != nil,
267311
dspec.Code.S3ObjectVersion != nil:
268-
269-
log := ackrtlog.FromContext(ctx)
270-
log.Debug("updating code.s3Bucket field is not currently supported.")
271-
272312
input.S3Bucket = dspec.Code.S3Bucket
273313
input.S3Key = dspec.Code.S3Key
274314
input.S3ObjectVersion = dspec.Code.S3ObjectVersion
@@ -384,6 +424,59 @@ func (rm *resourceManager) updateFunctionConcurrency(
384424
return nil
385425
}
386426

427+
// updateFunctionCodeSigningConfig calls PutFunctionCodeSigningConfig to update
428+
// it code signing configuration
429+
func (rm *resourceManager) updateFunctionCodeSigningConfig(
430+
ctx context.Context,
431+
desired *resource,
432+
) error {
433+
var err error
434+
rlog := ackrtlog.FromContext(ctx)
435+
exit := rlog.Trace("rm.updateFunctionCodeSigningConfig")
436+
defer exit(err)
437+
438+
if desired.ko.Spec.CodeSigningConfigARN == nil || *desired.ko.Spec.CodeSigningConfigARN == "" {
439+
return rm.deleteFunctionCodeSigningConfig(ctx, desired)
440+
}
441+
442+
dspec := desired.ko.Spec
443+
input := &svcsdk.PutFunctionCodeSigningConfigInput{
444+
FunctionName: aws.String(*dspec.Name),
445+
CodeSigningConfigArn: aws.String(*dspec.CodeSigningConfigARN),
446+
}
447+
448+
_, err = rm.sdkapi.PutFunctionCodeSigningConfigWithContext(ctx, input)
449+
rm.metrics.RecordAPICall("UPDATE", "PutFunctionCodeSigningConfig", err)
450+
if err != nil {
451+
return err
452+
}
453+
return nil
454+
}
455+
456+
// deleteFunctionCodeSigningConfig calls deleteFunctionCodeSigningConfig to update
457+
// it code signing configuration
458+
func (rm *resourceManager) deleteFunctionCodeSigningConfig(
459+
ctx context.Context,
460+
desired *resource,
461+
) error {
462+
var err error
463+
rlog := ackrtlog.FromContext(ctx)
464+
exit := rlog.Trace("rm.deleteFunctionCodeSigningConfig")
465+
defer exit(err)
466+
467+
dspec := desired.ko.Spec
468+
input := &svcsdk.DeleteFunctionCodeSigningConfigInput{
469+
FunctionName: aws.String(*dspec.Name),
470+
}
471+
472+
_, err = rm.sdkapi.DeleteFunctionCodeSigningConfigWithContext(ctx, input)
473+
rm.metrics.RecordAPICall("UPDATE", "DeleteFunctionCodeSigningConfig", err)
474+
if err != nil {
475+
return err
476+
}
477+
return nil
478+
}
479+
387480
// setResourceAdditionalFields will describe the fields that are not return by
388481
// GetFunctionConcurrency calls
389482
func (rm *resourceManager) setResourceAdditionalFields(
@@ -406,5 +499,18 @@ func (rm *resourceManager) setResourceAdditionalFields(
406499
return err
407500
}
408501
ko.Spec.ReservedConcurrentExecutions = getFunctionConcurrencyOutput.ReservedConcurrentExecutions
502+
503+
var getFunctionCodeSigningConfigOutput *svcsdk.GetFunctionCodeSigningConfigOutput
504+
getFunctionCodeSigningConfigOutput, err = rm.sdkapi.GetFunctionCodeSigningConfigWithContext(
505+
ctx,
506+
&svcsdk.GetFunctionCodeSigningConfigInput{
507+
FunctionName: ko.Spec.Name,
508+
},
509+
)
510+
rm.metrics.RecordAPICall("GET", "GetFunctionCodeSigningConfig", err)
511+
if err != nil {
512+
return err
513+
}
514+
ko.Spec.CodeSigningConfigARN = getFunctionCodeSigningConfigOutput.CodeSigningConfigArn
409515
return nil
410516
}

pkg/resource/function/sdk.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
if desired.ko.Spec.CodeSigningConfigARN != nil && *desired.ko.Spec.CodeSigningConfigARN == "" {
2+
input.CodeSigningConfigArn = nil
3+
}

test/e2e/resources/function.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ spec:
1313
runtime: python3.9
1414
handler: main
1515
description: function created by ACK lambda-controller e2e tests
16-
reservedConcurrentExecutions: $RESERVED_CONCURRENT_EXECUTIONS
16+
reservedConcurrentExecutions: $RESERVED_CONCURRENT_EXECUTIONS
17+
codeSigningConfigARN: "$CODE_SIGNING_CONFIG_ARN"

test/e2e/service_cleanup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,15 @@ def clean_up_and_delete_bucket(bucket_name: str):
7676

7777
def delete_sqs_queue(queue_url: str) -> str:
7878
region = get_region()
79-
sqs_client = boto3.resource('sqs', region_name=region)
80-
sqs_client.meta.client.delete_queue(
79+
sqs_client = boto3.client('sqs', region_name=region)
80+
sqs_client.delete_queue(
8181
QueueUrl=queue_url,
8282
)
8383
logging.info(f"Deleted SQS queue {queue_url}")
8484

8585
def delete_dynamodb_table(table_name: str) -> str:
8686
region = get_region()
87-
ddb_client = boto3.resource('dynamodb', region_name=region)
87+
ddb_client = boto3.client('dynamodb', region_name=region)
8888
ddb_client.delete_table(
8989
TableName=table_name,
9090
)

0 commit comments

Comments
 (0)