Skip to content

Commit bedf901

Browse files
authored
fix: Update support for Layers (#136)
**Description:** This PR adds the missing update feature for `Layers` property in Function resource. **Acknowledgement:** By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent a9250ad commit bedf901

File tree

6 files changed

+155
-65
lines changed

6 files changed

+155
-65
lines changed

apis/v1alpha1/ack-generate-metadata.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
ack_generate_info:
2-
build_date: "2024-04-16T23:40:41Z"
2+
build_date: "2024-04-18T22:18:38Z"
33
build_hash: 37f4ba2b5a121a8786bc516e9ec4aa0b8b590c7d
44
go_version: go1.21.6
55
version: v0.33.0-1-g37f4ba2

pkg/resource/function/hooks.go

+68-57
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,38 @@ func (rm *resourceManager) updateFunctionConfiguration(
179179
}
180180
}
181181

182+
if delta.DifferentAt("Spec.Environment") {
183+
environment := &svcsdk.Environment{}
184+
if dspec.Environment != nil {
185+
environment.Variables = dspec.Environment.DeepCopy().Variables
186+
}
187+
input.Environment = environment
188+
}
189+
190+
if delta.DifferentAt("Spec.EphemeralStorage") {
191+
ephemeralStorage := &svcsdk.EphemeralStorage{}
192+
if dspec.EphemeralStorage != nil {
193+
ephemeralStorageCopy := dspec.EphemeralStorage.DeepCopy()
194+
ephemeralStorage.Size = ephemeralStorageCopy.Size
195+
}
196+
input.EphemeralStorage = ephemeralStorage
197+
}
198+
199+
if delta.DifferentAt("Spec.FileSystemConfigs") {
200+
fileSystemConfigs := []*svcsdk.FileSystemConfig{}
201+
if len(dspec.FileSystemConfigs) > 0 {
202+
for _, elem := range dspec.FileSystemConfigs {
203+
elemCopy := elem.DeepCopy()
204+
fscElem := &svcsdk.FileSystemConfig{
205+
Arn: elemCopy.ARN,
206+
LocalMountPath: elemCopy.LocalMountPath,
207+
}
208+
fileSystemConfigs = append(fileSystemConfigs, fscElem)
209+
}
210+
input.FileSystemConfigs = fileSystemConfigs
211+
}
212+
}
213+
182214
if delta.DifferentAt("Spec.Handler") {
183215
if dspec.Handler != nil {
184216
input.Handler = aws.String(*dspec.Handler)
@@ -187,6 +219,19 @@ func (rm *resourceManager) updateFunctionConfiguration(
187219
}
188220
}
189221

222+
if delta.DifferentAt("Spec.ImageConfig") {
223+
if dspec.ImageConfig != nil && dspec.Code.ImageURI != nil && *dspec.Code.ImageURI != "" {
224+
imageConfig := &svcsdk.ImageConfig{}
225+
if dspec.ImageConfig != nil {
226+
imageConfigCopy := dspec.ImageConfig.DeepCopy()
227+
imageConfig.Command = imageConfigCopy.Command
228+
imageConfig.EntryPoint = imageConfigCopy.EntryPoint
229+
imageConfig.WorkingDirectory = imageConfigCopy.WorkingDirectory
230+
}
231+
input.ImageConfig = imageConfig
232+
}
233+
}
234+
190235
if delta.DifferentAt("Spec.KMSKeyARN") {
191236
if dspec.KMSKeyARN != nil {
192237
input.KMSKeyArn = aws.String(*dspec.KMSKeyARN)
@@ -195,11 +240,14 @@ func (rm *resourceManager) updateFunctionConfiguration(
195240
}
196241
}
197242

198-
if delta.DifferentAt("Spec.Role") {
199-
if dspec.Role != nil {
200-
input.Role = aws.String(*dspec.Role)
201-
} else {
202-
input.Role = aws.String("")
243+
if delta.DifferentAt("Spec.Layers") {
244+
layers := []*string{}
245+
if len(dspec.Layers) > 0 {
246+
for _, iter := range dspec.Layers {
247+
var elem string = *iter
248+
layers = append(layers, &elem)
249+
}
250+
input.Layers = layers
203251
}
204252
}
205253

@@ -211,47 +259,28 @@ func (rm *resourceManager) updateFunctionConfiguration(
211259
}
212260
}
213261

214-
if delta.DifferentAt("Spec.Timeout") {
215-
if dspec.Timeout != nil {
216-
input.Timeout = aws.Int64(*dspec.Timeout)
262+
if delta.DifferentAt("Spec.Role") {
263+
if dspec.Role != nil {
264+
input.Role = aws.String(*dspec.Role)
217265
} else {
218-
input.Timeout = aws.Int64(0)
219-
}
220-
}
221-
222-
if delta.DifferentAt("Spec.Environment") {
223-
environment := &svcsdk.Environment{}
224-
if dspec.Environment != nil {
225-
environment.Variables = dspec.Environment.DeepCopy().Variables
266+
input.Role = aws.String("")
226267
}
227-
input.Environment = environment
228268
}
229269

230-
if delta.DifferentAt("Spec.FileSystemConfigs") {
231-
fileSystemConfigs := []*svcsdk.FileSystemConfig{}
232-
if len(dspec.FileSystemConfigs) > 0 {
233-
for _, elem := range dspec.FileSystemConfigs {
234-
elemCopy := elem.DeepCopy()
235-
fscElem := &svcsdk.FileSystemConfig{
236-
Arn: elemCopy.ARN,
237-
LocalMountPath: elemCopy.LocalMountPath,
238-
}
239-
fileSystemConfigs = append(fileSystemConfigs, fscElem)
240-
}
241-
input.FileSystemConfigs = fileSystemConfigs
270+
if delta.DifferentAt(("Spec.SnapStart")) {
271+
snapStart := &svcsdk.SnapStart{}
272+
if dspec.SnapStart != nil {
273+
snapStartCopy := dspec.SnapStart.DeepCopy()
274+
snapStart.ApplyOn = snapStartCopy.ApplyOn
242275
}
276+
input.SnapStart = snapStart
243277
}
244278

245-
if delta.DifferentAt("Spec.ImageConfig") {
246-
if dspec.ImageConfig != nil && dspec.Code.ImageURI != nil && *dspec.Code.ImageURI != "" {
247-
imageConfig := &svcsdk.ImageConfig{}
248-
if dspec.ImageConfig != nil {
249-
imageConfigCopy := dspec.ImageConfig.DeepCopy()
250-
imageConfig.Command = imageConfigCopy.Command
251-
imageConfig.EntryPoint = imageConfigCopy.EntryPoint
252-
imageConfig.WorkingDirectory = imageConfigCopy.WorkingDirectory
253-
}
254-
input.ImageConfig = imageConfig
279+
if delta.DifferentAt("Spec.Timeout") {
280+
if dspec.Timeout != nil {
281+
input.Timeout = aws.Int64(*dspec.Timeout)
282+
} else {
283+
input.Timeout = aws.Int64(0)
255284
}
256285
}
257286

@@ -273,24 +302,6 @@ func (rm *resourceManager) updateFunctionConfiguration(
273302
input.VpcConfig = VPCConfig
274303
}
275304

276-
if delta.DifferentAt("Spec.EphemeralStorage") {
277-
ephemeralStorage := &svcsdk.EphemeralStorage{}
278-
if dspec.EphemeralStorage != nil {
279-
ephemeralStorageCopy := dspec.EphemeralStorage.DeepCopy()
280-
ephemeralStorage.Size = ephemeralStorageCopy.Size
281-
}
282-
input.EphemeralStorage = ephemeralStorage
283-
}
284-
285-
if delta.DifferentAt(("Spec.SnapStart")) {
286-
snapStart := &svcsdk.SnapStart{}
287-
if dspec.SnapStart != nil {
288-
snapStartCopy := dspec.SnapStart.DeepCopy()
289-
snapStart.ApplyOn = snapStartCopy.ApplyOn
290-
}
291-
input.SnapStart = snapStart
292-
}
293-
294305
_, err = rm.sdkapi.UpdateFunctionConfigurationWithContext(ctx, input)
295306
rm.metrics.RecordAPICall("UPDATE", "UpdateFunctionConfiguration", err)
296307
if err != nil {

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

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
if resp.Code != nil {
2-
// We need to keep the desired .Code s3Bucket s3Key and s3ObjectVersion
3-
// part of the function's spec. So instead of setting Spec.Code to nil
4-
// we only set ImageURI
5-
//
6-
// When adopting a Function resource, Spec.Code field should be manually
7-
// initialised before injecting ImageURI.
82
if ko.Spec.Code == nil {
93
ko.Spec.Code = &svcapitypes.FunctionCode{}
104
}
@@ -13,7 +7,8 @@
137
}
148
}
159
if resp.Configuration.Layers != nil {
16-
f16 := []*svcapitypes.Layer{}
10+
f17 := []*svcapitypes.Layer{}
11+
layer := []*string{}
1712
for _, f16iter := range resp.Configuration.Layers {
1813
f16elem := &svcapitypes.Layer{}
1914
if f16iter.Arn != nil {
@@ -29,7 +24,9 @@
2924
f16elem.SigningProfileVersionARN = f16iter.SigningProfileVersionArn
3025
}
3126
f16 = append(f16, f16elem)
27+
layer = append(layer, f16iter.Arn)
3228
}
29+
ko.Spec.Layers = layer
3330
ko.Status.LayerStatuses = f16
3431
} else {
3532
ko.Status.LayerStatuses = nil
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
s3Bucket: $BUCKET_NAME
11+
s3Key: $LAMBDA_FILE_NAME
12+
role: $LAMBDA_ROLE
13+
runtime: python3.9
14+
handler: main
15+
layers: [$LAYERS]
16+
description: function created by ACK lambda-controller e2e tests

test/e2e/tests/test_function.py

+63
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,69 @@ def test_function_features(self, lambda_client):
664664

665665
# Check Lambda function doesn't exist
666666
assert not lambda_validator.function_exists(resource_name)
667+
668+
def test_function_layers(self, lambda_client):
669+
resource_name = random_suffix_name("functionlayers", 24)
670+
671+
resources = get_bootstrap_resources()
672+
logging.debug(resources)
673+
674+
replacements = REPLACEMENT_VALUES.copy()
675+
replacements["FUNCTION_NAME"] = resource_name
676+
replacements["BUCKET_NAME"] = resources.FunctionsBucket.name
677+
replacements["LAMBDA_ROLE"] = resources.EICRole.arn
678+
replacements["LAMBDA_FILE_NAME"] = LAMBDA_FUNCTION_FILE_ZIP
679+
replacements["AWS_REGION"] = get_region()
680+
replacements["LAYERS"] = "arn:aws:lambda:us-west-2:336392948345:layer:AWSSDKPandas-Python310:14"
681+
682+
# Load Lambda CR
683+
resource_data = load_lambda_resource(
684+
"function_layers",
685+
additional_replacements=replacements,
686+
)
687+
logging.debug(resource_data)
688+
689+
# Create k8s resource
690+
ref = k8s.CustomResourceReference(
691+
CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL,
692+
resource_name, namespace="default",
693+
)
694+
k8s.create_custom_resource(ref, resource_data)
695+
cr = k8s.wait_resource_consumed_by_controller(ref)
696+
697+
assert cr is not None
698+
assert k8s.get_resource_exists(ref)
699+
700+
time.sleep(CREATE_WAIT_AFTER_SECONDS)
701+
702+
cr = k8s.wait_resource_consumed_by_controller(ref)
703+
704+
lambda_validator = LambdaValidator(lambda_client)
705+
706+
# Check Lambda function exists
707+
assert lambda_validator.function_exists(resource_name)
708+
709+
# Update cr
710+
layers_list = ["arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:68", "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:52"]
711+
cr["spec"]["layers"] = layers_list
712+
713+
#Patch k8s resource
714+
k8s.patch_custom_resource(ref, cr)
715+
time.sleep(UPDATE_WAIT_AFTER_SECONDS)
716+
717+
#Check function_snapstart update fields
718+
function = lambda_validator.get_function(resource_name)
719+
for i in range(len(function["Configuration"]["Layers"])) :
720+
assert function["Configuration"]["Layers"][i]["Arn"] == layers_list[i]
721+
722+
# Delete k8s resource
723+
_, deleted = k8s.delete_custom_resource(ref)
724+
assert deleted is True
725+
726+
time.sleep(DELETE_WAIT_AFTER_SECONDS)
727+
728+
# Check Lambda function doesn't exist
729+
assert not lambda_validator.function_exists(resource_name)
667730

668731
def test_function_event_invoke_config(self, lambda_client):
669732
resource_name = random_suffix_name("lambda-function", 24)

0 commit comments

Comments
 (0)