Skip to content

Commit c7ea860

Browse files
authored
Prevent GetFunctionCodeSigningConfig calls when a functions packageType is Image (#21)
Issue #, if available: aws-controllers-k8s/community#1117 Description of changes: Functions code signing config should only be called when a function is created using an s3bucket and a key. Functions created using a container image cannot get a code signing configuration. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 33fd9c7 commit c7ea860

File tree

10 files changed

+203
-24
lines changed

10 files changed

+203
-24
lines changed
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
ack_generate_info:
2-
build_date: "2022-04-27T16:40:07Z"
3-
build_hash: 141cb9db73f881228ea20e572de3ba9df19d5b6f
2+
build_date: "2022-05-13T16:30:20Z"
3+
build_hash: 302c31fcf0cb7eacf535af8a65569882b3ee0d7c
44
go_version: go1.18.1
5-
version: v0.18.4-4-g141cb9d-dirty
5+
version: v0.18.4-3-g302c31f
66
api_directory_checksum: 7c4e0f8971a8ab06389e98b21c00eddad87366f3
77
api_version: v1alpha1
88
aws_sdk_go_version: v1.42.0
99
generator_config_info:
10-
file_checksum: c48a2acfc8da0b28ee9b81745b9af773d10c7f71
10+
file_checksum: 2559b6bfec9b6a5155e6119d9c649a8a4586b188
1111
original_file_name: generator.yaml
1212
last_modification:
1313
reason: API generation

apis/v1alpha1/generator.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ resources:
3737
GetFunction:
3838
input_fields:
3939
FunctionName: Name
40+
exceptions:
41+
terminal_codes:
42+
- InvalidParameterValueException
4043
hooks:
4144
delta_pre_compare:
4245
code: customPreCompare(delta, a, b)

generator.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ resources:
3737
GetFunction:
3838
input_fields:
3939
FunctionName: Name
40+
exceptions:
41+
terminal_codes:
42+
- InvalidParameterValueException
4043
hooks:
4144
delta_pre_compare:
4245
code: customPreCompare(delta, a, b)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/aws-controllers-k8s/lambda-controller
33
go 1.17
44

55
require (
6-
github.com/aws-controllers-k8s/runtime v0.18.4
6+
github.com/aws-controllers-k8s/runtime v0.18.5
77
github.com/aws/aws-sdk-go v1.42.0
88
github.com/go-logr/logr v1.2.0
99
github.com/spf13/pflag v1.0.5

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
6464
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
6565
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
6666
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
67-
github.com/aws-controllers-k8s/runtime v0.18.4 h1:iwLYNwhbuiWZrHPoulGj75oT+alE91wCNkF1FUELiAw=
68-
github.com/aws-controllers-k8s/runtime v0.18.4/go.mod h1:oA8ML1/LL3chPn26P6SzBNu1CUI2nekB+PTqykNs0qU=
67+
github.com/aws-controllers-k8s/runtime v0.18.5 h1:P8oMQagd45JQaloVQ+duuJzMw4ii8/IXYIgPN2EjU+8=
68+
github.com/aws-controllers-k8s/runtime v0.18.5/go.mod h1:oA8ML1/LL3chPn26P6SzBNu1CUI2nekB+PTqykNs0qU=
6969
github.com/aws/aws-sdk-go v1.42.0 h1:BMZws0t8NAhHFsfnT3B40IwD13jVDG5KerlRksctVIw=
7070
github.com/aws/aws-sdk-go v1.42.0/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
7171
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=

pkg/resource/function/hooks.go

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,19 @@ import (
1818
"errors"
1919
"time"
2020

21-
svcapitypes "github.com/aws-controllers-k8s/lambda-controller/apis/v1alpha1"
2221
ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
22+
ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors"
2323
ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue"
2424
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
2525
"github.com/aws/aws-sdk-go/aws"
2626
svcsdk "github.com/aws/aws-sdk-go/service/lambda"
27+
28+
svcapitypes "github.com/aws-controllers-k8s/lambda-controller/apis/v1alpha1"
2729
)
2830

2931
var (
30-
ErrFunctionPending = errors.New("Function in 'Pending' state, cannot be modified or deleted")
32+
ErrFunctionPending = errors.New("Function in 'Pending' state, cannot be modified or deleted")
33+
ErrCannotSetFunctionCSC = errors.New("cannot set function code signing config when package type is Image")
3134
)
3235

3336
var (
@@ -98,10 +101,16 @@ func (rm *resourceManager) customUpdateFunction(
98101
return nil, err
99102
}
100103
}
104+
101105
if delta.DifferentAt("Spec.CodeSigningConfigARN") {
102-
err = rm.updateFunctionCodeSigningConfig(ctx, desired)
103-
if err != nil {
104-
return nil, err
106+
if desired.ko.Spec.PackageType != nil && *desired.ko.Spec.PackageType == "Image" &&
107+
desired.ko.Spec.CodeSigningConfigARN != nil && *desired.ko.Spec.CodeSigningConfigARN != "" {
108+
return nil, ackerr.NewTerminalError(ErrCannotSetFunctionCSC)
109+
} else {
110+
err = rm.updateFunctionCodeSigningConfig(ctx, desired)
111+
if err != nil {
112+
return nil, err
113+
}
105114
}
106115
}
107116

@@ -500,17 +509,23 @@ func (rm *resourceManager) setResourceAdditionalFields(
500509
}
501510
ko.Spec.ReservedConcurrentExecutions = getFunctionConcurrencyOutput.ReservedConcurrentExecutions
502511

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
512+
if ko.Spec.PackageType != nil && *ko.Spec.PackageType == "Zip" {
513+
var getFunctionCodeSigningConfigOutput *svcsdk.GetFunctionCodeSigningConfigOutput
514+
getFunctionCodeSigningConfigOutput, err = rm.sdkapi.GetFunctionCodeSigningConfigWithContext(
515+
ctx,
516+
&svcsdk.GetFunctionCodeSigningConfigInput{
517+
FunctionName: ko.Spec.Name,
518+
},
519+
)
520+
rm.metrics.RecordAPICall("GET", "GetFunctionCodeSigningConfig", err)
521+
if err != nil {
522+
return err
523+
}
524+
ko.Spec.CodeSigningConfigARN = getFunctionCodeSigningConfigOutput.CodeSigningConfigArn
525+
}
526+
if ko.Spec.PackageType != nil && *ko.Spec.PackageType == "Image" &&
527+
ko.Spec.CodeSigningConfigARN != nil && *ko.Spec.CodeSigningConfigARN != "" {
528+
return ackerr.NewTerminalError(ErrCannotSetFunctionCSC)
513529
}
514-
ko.Spec.CodeSigningConfigARN = getFunctionCodeSigningConfigOutput.CodeSigningConfigArn
515530
return nil
516531
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: lambda.services.k8s.aws/v1alpha1
2+
kind: Function
3+
metadata:
4+
name: $FUNCTION_NAME
5+
annotations:
6+
services.k8s.aws/region: $AWS_REGION
7+
spec:
8+
name: $FUNCTION_NAME
9+
code:
10+
imageURI: $IMAGE_URL
11+
role: $LAMBDA_ROLE
12+
description: function created by ACK lambda-controller e2e tests
13+
packageType: Image
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM public.ecr.aws/lambda/python:3.8
2+
3+
COPY main.py main.py
4+
5+
CMD [ "main.handler" ]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
AWS_REGION ?= "us-west-2"
2+
ECR_REPOSITORY ?= ack-e2e-testing-lambda-controller
3+
IMAGE_TAG ?= v1
4+
5+
AWS_ACCOUNT_ID ?= $(shell aws sts get-caller-identity --query "Account" --output text)
6+
IMAGE_URL ?= $(AWS_ACCOUNT_ID).dkr.ecr.us-west-2.amazonaws.com/$(ECR_REPOSITORY):$(IMAGE_TAG)
7+
8+
build-image:
9+
docker build -t $(IMAGE_URL) .
10+
11+
publish-image:
12+
docker push $(IMAGE_URL)
13+
14+
create-ecr-repository:
15+
aws ecr create-repository --region $(AWS_REGION) --repository-name $(ECR_REPOSITORY) >/dev/null
16+
17+
all: build-image publish-image

test/e2e/tests/test_function.py

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from typing import Dict, Tuple
2222

2323
from acktest.resources import random_suffix_name
24-
from acktest.aws.identity import get_region
24+
from acktest.aws.identity import get_region, get_account_id
2525
from acktest.k8s import resource as k8s
2626
from e2e import service_marker, CRD_GROUP, CRD_VERSION, load_lambda_resource
2727
from e2e.replacement_values import REPLACEMENT_VALUES
@@ -33,6 +33,12 @@
3333
UPDATE_WAIT_AFTER_SECONDS = 25
3434
DELETE_WAIT_AFTER_SECONDS = 25
3535

36+
37+
def get_testing_image_url():
38+
aws_region = get_region()
39+
account_id = get_account_id()
40+
return f"{account_id}.dkr.ecr.{aws_region}.amazonaws.com/ack-e2e-testing-lambda-controller:v1"
41+
3642
@pytest.fixture(scope="module")
3743
def lambda_client():
3844
return boto3.client("lambda")
@@ -313,3 +319,120 @@ def test_function_code_signing_config(self, lambda_client, code_signing_config):
313319
# Check Lambda function doesn't exist
314320
exists = self.function_exists(lambda_client, resource_name)
315321
assert not exists
322+
323+
def test_function_package_type_image(self, lambda_client, code_signing_config):
324+
resource_name = random_suffix_name("lambda-function", 24)
325+
326+
resources = get_bootstrap_resources()
327+
328+
replacements = REPLACEMENT_VALUES.copy()
329+
replacements["FUNCTION_NAME"] = resource_name
330+
replacements["LAMBDA_ROLE"] = resources.LambdaBasicRoleARN
331+
replacements["AWS_REGION"] = get_region()
332+
replacements["IMAGE_URL"] = get_testing_image_url()
333+
334+
# Load Lambda CR
335+
resource_data = load_lambda_resource(
336+
"function_package_type_image",
337+
additional_replacements=replacements,
338+
)
339+
logging.debug(resource_data)
340+
341+
# Create k8s resource
342+
ref = k8s.CustomResourceReference(
343+
CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL,
344+
resource_name, namespace="default",
345+
)
346+
k8s.create_custom_resource(ref, resource_data)
347+
cr = k8s.wait_resource_consumed_by_controller(ref)
348+
349+
assert cr is not None
350+
assert k8s.get_resource_exists(ref)
351+
352+
time.sleep(CREATE_WAIT_AFTER_SECONDS)
353+
354+
cr = k8s.wait_resource_consumed_by_controller(ref)
355+
356+
# Check Lambda function exists
357+
exists = self.function_exists(lambda_client, resource_name)
358+
assert exists
359+
360+
# Delete k8s resource
361+
_, deleted = k8s.delete_custom_resource(ref)
362+
assert deleted is True
363+
364+
time.sleep(DELETE_WAIT_AFTER_SECONDS)
365+
366+
# Check Lambda function doesn't exist
367+
exists = self.function_exists(lambda_client, resource_name)
368+
assert not exists
369+
370+
def test_function_package_type_image_with_signing_config(self, lambda_client, code_signing_config):
371+
resource_name = random_suffix_name("lambda-function", 24)
372+
373+
resources = get_bootstrap_resources()
374+
375+
replacements = REPLACEMENT_VALUES.copy()
376+
replacements["FUNCTION_NAME"] = resource_name
377+
replacements["LAMBDA_ROLE"] = resources.LambdaBasicRoleARN
378+
replacements["AWS_REGION"] = get_region()
379+
replacements["IMAGE_URL"] = get_testing_image_url()
380+
381+
# Load Lambda CR
382+
resource_data = load_lambda_resource(
383+
"function_package_type_image",
384+
additional_replacements=replacements,
385+
)
386+
logging.debug(resource_data)
387+
388+
# Create k8s resource
389+
ref = k8s.CustomResourceReference(
390+
CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL,
391+
resource_name, namespace="default",
392+
)
393+
k8s.create_custom_resource(ref, resource_data)
394+
cr = k8s.wait_resource_consumed_by_controller(ref)
395+
396+
assert cr is not None
397+
assert k8s.get_resource_exists(ref)
398+
399+
time.sleep(CREATE_WAIT_AFTER_SECONDS)
400+
401+
cr = k8s.wait_resource_consumed_by_controller(ref)
402+
403+
# Check Lambda function exists
404+
exists = self.function_exists(lambda_client, resource_name)
405+
assert exists
406+
407+
# Add signing configuration
408+
cr["spec"]["codeSigningConfigARN"] = "random-csc"
409+
k8s.patch_custom_resource(ref, cr)
410+
411+
time.sleep(UPDATE_WAIT_AFTER_SECONDS)
412+
413+
cr = k8s.wait_resource_consumed_by_controller(ref)
414+
# assert condition
415+
assert k8s.assert_condition_state_message(
416+
ref,
417+
"ACK.Terminal",
418+
"True",
419+
"cannot set function code signing config when package type is Image",
420+
)
421+
422+
cr = k8s.wait_resource_consumed_by_controller(ref)
423+
424+
# Remove signing configuration
425+
cr["spec"]["codeSigningConfigARN"] = ""
426+
k8s.patch_custom_resource(ref, cr)
427+
428+
time.sleep(UPDATE_WAIT_AFTER_SECONDS)
429+
430+
# Delete k8s resource
431+
_, deleted = k8s.delete_custom_resource(ref)
432+
assert deleted is True
433+
434+
time.sleep(DELETE_WAIT_AFTER_SECONDS)
435+
436+
# Check Lambda function doesn't exist
437+
exists = self.function_exists(lambda_client, resource_name)
438+
assert not exists

0 commit comments

Comments
 (0)