From 0f9204d35d6e1521df8785ef81edce7ced0a43be Mon Sep 17 00:00:00 2001 From: Vandita2020 Date: Thu, 14 Mar 2024 10:47:26 -0700 Subject: [PATCH] Support for update architecture field --- pkg/resource/function/hooks.go | 47 ++++++++++++++ pkg/resource/function/sdk.go | 11 ++++ .../sdk_read_one_post_set_output.go.tpl | 11 ++++ .../e2e/resources/function_architectures.yaml | 18 +++++ test/e2e/tests/test_function.py | 65 +++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 test/e2e/resources/function_architectures.yaml diff --git a/pkg/resource/function/hooks.go b/pkg/resource/function/hooks.go index 22c32ab2..a5dd5ead 100644 --- a/pkg/resource/function/hooks.go +++ b/pkg/resource/function/hooks.go @@ -102,6 +102,12 @@ func (rm *resourceManager) customUpdateFunction( } } } + if delta.DifferentAt("Spec.Architectures") { + err = rm.updateFunctionArchitectures(ctx, desired, latest) + if err != nil { + return nil, err + } + } // Only try to update Spec.Code or Spec.Configuration at once. It is // not correct to sequentially call UpdateFunctionConfiguration and @@ -343,6 +349,47 @@ func (rm *resourceManager) updateFunctionTags( return nil } +// updateFunctionArchitectures calls UpdateFunctionCode to update architecture for lambda +// function code. +func (rm *resourceManager) updateFunctionArchitectures( + ctx context.Context, + desired *resource, + latest *resource, +) error { + var err error + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.updateFunctionArchitectures") + defer exit(err) + + dspec := desired.ko.Spec + input := &svcsdk.UpdateFunctionCodeInput{ + FunctionName: aws.String(*dspec.Name), + } + + if dspec.Architectures != nil { + input.Architectures = dspec.Architectures + } else { + input.Architectures = nil + } + + if latest.ko.Spec.Code != nil { + if latest.ko.Spec.PackageType != nil && *latest.ko.Spec.PackageType == "Image" { + input.ImageUri = latest.ko.Spec.Code.ImageURI + } else if latest.ko.Spec.PackageType != nil && *latest.ko.Spec.PackageType == "Zip" { + input.S3Bucket = latest.ko.Spec.Code.S3Bucket + input.S3Key = latest.ko.Spec.Code.S3Key + } + } + + _, err = rm.sdkapi.UpdateFunctionCodeWithContext(ctx, input) + rm.metrics.RecordAPICall("UPDATE", "UpdateFunctionArchitectures", err) + if err != nil { + return err + } + + return nil +} + // updateFunctionsCode calls UpdateFunctionCode to update a specific lambda // function code. func (rm *resourceManager) updateFunctionCode( diff --git a/pkg/resource/function/sdk.go b/pkg/resource/function/sdk.go index 196aa1c3..8116ec9e 100644 --- a/pkg/resource/function/sdk.go +++ b/pkg/resource/function/sdk.go @@ -117,6 +117,17 @@ func (rm *resourceManager) sdkFind( ko.Spec.Code.ImageURI = resp.Code.ImageUri } } + if resp.Configuration.Architectures != nil { + f1 := []*string{} + for _, f1iter := range resp.Configuration.Architectures { + var f1elem string + f1elem = *f1iter + f1 = append(f1, &f1elem) + } + ko.Spec.Architectures = f1 + } else { + ko.Spec.Architectures = nil + } if resp.Configuration.CodeSha256 != nil { ko.Status.CodeSHA256 = resp.Configuration.CodeSha256 } else { diff --git a/templates/hooks/function/sdk_read_one_post_set_output.go.tpl b/templates/hooks/function/sdk_read_one_post_set_output.go.tpl index 2c0c8953..d5f3b6ea 100644 --- a/templates/hooks/function/sdk_read_one_post_set_output.go.tpl +++ b/templates/hooks/function/sdk_read_one_post_set_output.go.tpl @@ -12,6 +12,17 @@ ko.Spec.Code.ImageURI = resp.Code.ImageUri } } + if resp.Configuration.Architectures != nil { + f1 := []*string{} + for _, f1iter := range resp.Configuration.Architectures { + var f1elem string + f1elem = *f1iter + f1 = append(f1, &f1elem) + } + ko.Spec.Architectures = f1 + } else { + ko.Spec.Architectures = nil + } if resp.Configuration.CodeSha256 != nil { ko.Status.CodeSHA256 = resp.Configuration.CodeSha256 } else { diff --git a/test/e2e/resources/function_architectures.yaml b/test/e2e/resources/function_architectures.yaml new file mode 100644 index 00000000..b28db087 --- /dev/null +++ b/test/e2e/resources/function_architectures.yaml @@ -0,0 +1,18 @@ +apiVersion: lambda.services.k8s.aws/v1alpha1 +kind: Function +metadata: + name: $FUNCTION_NAME + annotations: + services.k8s.aws/region: $AWS_REGION +spec: + name: $FUNCTION_NAME + code: + s3Bucket: $BUCKET_NAME + s3Key: $LAMBDA_FILE_NAME + role: $LAMBDA_ROLE + architectures: [$ARCHITECTURES] + runtime: python3.9 + handler: main + description: function created by ACK lambda-controller e2e tests + reservedConcurrentExecutions: $RESERVED_CONCURRENT_EXECUTIONS + codeSigningConfigARN: "$CODE_SIGNING_CONFIG_ARN" \ No newline at end of file diff --git a/test/e2e/tests/test_function.py b/test/e2e/tests/test_function.py index c908c007..58f4fc13 100644 --- a/test/e2e/tests/test_function.py +++ b/test/e2e/tests/test_function.py @@ -539,6 +539,71 @@ def test_function_snapstart(self, lambda_client): # Check Lambda function doesn't exist assert not lambda_validator.function_exists(resource_name) + def test_function_architecture(self, lambda_client): + resource_name = random_suffix_name("functionsarchitecture", 24) + + resources = get_bootstrap_resources() + logging.debug(resources) + + replacements = REPLACEMENT_VALUES.copy() + replacements["FUNCTION_NAME"] = resource_name + replacements["BUCKET_NAME"] = resources.FunctionsBucket.name + replacements["LAMBDA_ROLE"] = resources.BasicRole.arn + replacements["LAMBDA_FILE_NAME"] = LAMBDA_FUNCTION_FILE_ZIP + replacements["RESERVED_CONCURRENT_EXECUTIONS"] = "0" + replacements["CODE_SIGNING_CONFIG_ARN"] = "" + replacements["AWS_REGION"] = get_region() + replacements["ARCHITECTURES"] = 'x86_64' + + # Load Lambda CR + resource_data = load_lambda_resource( + "function_architectures", + additional_replacements=replacements, + ) + logging.debug(resource_data) + + # Create k8s resource + ref = k8s.CustomResourceReference( + CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, + resource_name, namespace="default", + ) + k8s.create_custom_resource(ref, resource_data) + cr = k8s.wait_resource_consumed_by_controller(ref) + + assert cr is not None + assert k8s.get_resource_exists(ref) + + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + cr = k8s.wait_resource_consumed_by_controller(ref) + + lambda_validator = LambdaValidator(lambda_client) + + # Check Lambda function exists + assert lambda_validator.function_exists(resource_name) + + # Update cr + cr["spec"]["architectures"] = ['arm64'] + cr["spec"]["code"]["s3Bucket"] = resources.FunctionsBucket.name + cr["spec"]["code"]["s3Key"] = LAMBDA_FUNCTION_FILE_ZIP + + #Patch k8s resource + k8s.patch_custom_resource(ref, cr) + time.sleep(UPDATE_WAIT_AFTER_SECONDS) + + #Check function_snapstart update fields + function = lambda_validator.get_function(resource_name) + assert function["Configuration"]["Architectures"] == ['arm64'] + + # Delete k8s resource + _, deleted = k8s.delete_custom_resource(ref) + assert deleted is True + + time.sleep(DELETE_WAIT_AFTER_SECONDS) + + # Check Lambda function doesn't exist + assert not lambda_validator.function_exists(resource_name) + def test_function_event_invoke_config(self, lambda_client): resource_name = random_suffix_name("lambda-function", 24)