diff --git a/.github/workflows/java-lambda-canary.yml b/.github/workflows/java-lambda-canary.yml new file mode 100644 index 00000000..1450f103 --- /dev/null +++ b/.github/workflows/java-lambda-canary.yml @@ -0,0 +1,29 @@ +## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +## SPDX-License-Identifier: Apache-2.0 + +## This workflow aims to run the Application Signals Java end-to-end tests as a canary to +## test the artifacts for Application Signals enablement on Lambda. +name: Java Lambda Enablement Canary Testing +on: + schedule: + - cron: '20,45 * * * *' # run the workflow at 20th and 45th minute of every hour + workflow_dispatch: # be able to run the workflow on demand + +permissions: + id-token: write + contents: read + +jobs: + github: + strategy: + fail-fast: false + matrix: + aws-region: ['af-south-1','ap-east-1','ap-northeast-1','ap-northeast-2','ap-northeast-3','ap-south-1','ap-south-2','ap-southeast-1', + 'ap-southeast-2','ap-southeast-3','ap-southeast-4','ca-central-1','eu-central-1','eu-central-2','eu-north-1', + 'eu-south-1','eu-south-2','eu-west-1','eu-west-2','eu-west-3','il-central-1','me-central-1','me-south-1', 'sa-east-1', + 'us-east-1','us-east-2', 'us-west-1', 'us-west-2'] + uses: ./.github/workflows/java-lambda-retry.yml + secrets: inherit + with: + aws-region: ${{ matrix.aws-region }} + caller-workflow-name: 'appsignals-java-e2e-lambda-canary-test' diff --git a/.github/workflows/java-lambda-retry.yml b/.github/workflows/java-lambda-retry.yml new file mode 100644 index 00000000..69438338 --- /dev/null +++ b/.github/workflows/java-lambda-retry.yml @@ -0,0 +1,57 @@ +## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +## SPDX-License-Identifier: Apache-2.0 + +# This is a reusable workflow for running the Java Lambda Canary with retries. +# It is meant to be called from another workflow. +# Read more about reusable workflows: https://docs.github.com/en/actions/using-workflows/reusing-workflows#overview +name: Java Lambda Default Retry +on: + workflow_call: + inputs: + aws-region: + required: true + type: string + caller-workflow-name: + required: true + type: string + +permissions: + id-token: write + contents: read + +jobs: + java-lambda-attempt-1: + uses: ./.github/workflows/java-lambda-test.yml + secrets: inherit + with: + aws-region: ${{ inputs.aws-region }} + caller-workflow-name: ${{ inputs.caller-workflow-name }} + + java-lambda-attempt-2: + needs: [ java-lambda-attempt-1 ] + if: ${{ needs.java-lambda-attempt-1.outputs.job-started != 'true' }} + uses: ./.github/workflows/java-lambda-test.yml + secrets: inherit + with: + aws-region: ${{ inputs.aws-region }} + caller-workflow-name: ${{ inputs.caller-workflow-name }} + + publish-metric-attempt-1: + needs: [ java-lambda-attempt-1, java-lambda-attempt-2 ] + if: always() + uses: ./.github/workflows/enablement-test-publish-result.yml + secrets: inherit + with: + aws-region: ${{ inputs.aws-region }} + caller-workflow-name: ${{ inputs.caller-workflow-name }} + validation-result: ${{ needs.java-lambda-attempt-1.outputs.validation-result || needs.java-lambda-attempt-2.outputs.validation-result }} + + publish-metric-attempt-2: + needs: [ java-lambda-attempt-1, java-lambda-attempt-2, publish-metric-attempt-1 ] + if: ${{ always() && needs.publish-metric-attempt-1.outputs.job-started != 'true' }} + uses: ./.github/workflows/enablement-test-publish-result.yml + secrets: inherit + with: + aws-region: ${{ inputs.aws-region }} + caller-workflow-name: ${{ inputs.caller-workflow-name }} + validation-result: ${{ needs.java-lambda-attempt-1.outputs.validation-result || needs.java-lambda-attempt-2.outputs.validation-result }} \ No newline at end of file diff --git a/.github/workflows/java-lambda-test.yml b/.github/workflows/java-lambda-test.yml new file mode 100644 index 00000000..0e512666 --- /dev/null +++ b/.github/workflows/java-lambda-test.yml @@ -0,0 +1,231 @@ +## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +## SPDX-License-Identifier: Apache-2.0 + +# This is a reusable workflow for running the Java Enablement Canary test for Application Signals on Lambda. +# It is meant to be called from another workflow. +# Read more about reusable workflows: https://docs.github.com/en/actions/using-workflows/reusing-workflows#overview +name: Java Lambda Default Use Case +on: + workflow_call: + inputs: + aws-region: + required: true + type: string + caller-workflow-name: + required: true + type: string + outputs: + job-started: + value: ${{ jobs.java-lambda-default.outputs.job-started }} + validation-result: + value: ${{ jobs.java-lambda-default.outputs.validation-result }} + +permissions: + id-token: write + contents: read + +env: + E2E_TEST_AWS_REGION: ${{ inputs.aws-region }} + CALLER_WORKFLOW_NAME: ${{ inputs.caller-workflow-name }} + E2E_TEST_ACCOUNT_ID: ${{ secrets.APPLICATION_SIGNALS_E2E_TEST_ACCOUNT_ID }} + E2E_TEST_ROLE_NAME: ${{ secrets.APPLICATION_SIGNALS_E2E_TEST_ROLE_NAME }} + METRIC_NAMESPACE: ApplicationSignals + LOG_GROUP_NAME: /aws/application-signals/data + SAMPLE_APP_ZIP: s3://aws-appsignals-sample-app-prod-${{ inputs.aws-region }}/java-lambda-function.zip + TEST_RESOURCES_FOLDER: ${GITHUB_WORKSPACE} + STAGING_S3_BUCKET: ${{ secrets.STAGING_BUCKET_NAME }} + IS_CANARY: false + +jobs: + java-lambda-default: + runs-on: ubuntu-latest + timeout-minutes: 30 + outputs: + job-started: ${{ steps.job-started.outputs.job-started }} + validation-result: ${{ steps.validation-result.outputs.validation-result }} + steps: + - name: Check if the job started + id: job-started + run: echo "job-started=true" >> $GITHUB_OUTPUT + + - uses: actions/checkout@v4 + with: + repository: 'aws-observability/aws-application-signals-test-framework' + ref: ${{ env.CALLER_WORKFLOW_NAME == 'main-build' && 'main' || github.ref }} + fetch-depth: 0 + + # We initialize Gradlew Daemon early on during the workflow because sometimes initialization + # fails due to transient issues. If it fails here, then we will try again later before the validators + - name: Initiate Gradlew Daemon + id: initiate-gradlew + uses: ./.github/workflows/actions/execute_and_retry + continue-on-error: true + with: + command: "./gradlew :validator:build" + cleanup: "./gradlew clean" + max_retry: 3 + sleep_time: 60 + + - name: Generate testing id + run: echo TESTING_ID="${{ github.run_id }}-${{ github.run_number }}-${RANDOM}" >> $GITHUB_ENV + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ env.E2E_TEST_ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }} + aws-region: us-east-1 + + - name: Retrieve account + uses: aws-actions/aws-secretsmanager-get-secrets@v1 + with: + secret-ids: + ACCOUNT_ID, region-account/${{ env.E2E_TEST_AWS_REGION }} + + - name: Configure AWS Credentials + if: ${{ github.event.repository.name == 'aws-application-signals-test-framework' }} + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ env.ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }} + aws-region: ${{ env.E2E_TEST_AWS_REGION }} + + - name: Set Lambda Layer artifact directory path + run: echo ARTIFACTS_DIR="${{ github.workspace }}/lambda_artifacts" >> $GITHUB_ENV + + - name: Download Lambda Layer and Function artifacts for E2E Test + if: ${{ env.CALLER_WORKFLOW_NAME != 'appsignals-java-e2e-lambda-canary-test' }} + run: | + aws s3 cp s3://${{ env.STAGING_S3_BUCKET }}/adot-java-lambda-layer-${{ github.run_id }}.zip ${{ env.ARTIFACTS_DIR }}/layer.zip + aws s3 cp ${{ env.SAMPLE_APP_ZIP }} ${{ env.ARTIFACTS_DIR }}/java-function.zip + + - name: Set Canary Environment Variable + if: ${{ env.CALLER_WORKFLOW_NAME == 'appsignals-java-e2e-lambda-canary-test' }} + run: | + echo IS_CANARY=true >> $GITHUB_ENV | + aws s3 cp ${{ env.SAMPLE_APP_ZIP }} ${{ env.ARTIFACTS_DIR }}/javafunction.zip + + - name: Set up terraform + uses: ./.github/workflows/actions/execute_and_retry + with: + command: "wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg" + post-command: 'echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list + && sudo apt update && sudo apt install terraform' + sleep_time: 60 + + - name: Initiate Terraform + uses: ./.github/workflows/actions/execute_and_retry + with: + command: "cd ${{ env.TEST_RESOURCES_FOLDER }}/terraform/java/lambda/lambda && terraform init && terraform validate" + cleanup: "rm -rf .terraform && rm -rf .terraform.lock.hcl" + max_retry: 6 + sleep_time: 60 + + - name: Get terraform Lambda function name + shell: bash + run: | + echo TERRAFORM_LAMBDA_FUNCTION_NAME="AdotLambdaJavaSampleApp-${{ github.run_id }}"| + tee --append $GITHUB_ENV + + - name: Apply terraform + uses: ./.github/workflows/actions/execute_and_retry + with: + command: 'cd ${{ env.TEST_RESOURCES_FOLDER }}/terraform/java/lambda/lambda && terraform apply -auto-approve + -var="sdk_layer_name=AWSOpenTelemetryDistroJava-${{ github.run_id }}" + -var="function_name=${{env.TERRAFORM_LAMBDA_FUNCTION_NAME}}" + -var="layer_artifacts_directory=${{ env.ARTIFACTS_DIR }}" + -var="region=${{ env.E2E_TEST_AWS_REGION }}" + -var="is_canary=${{ env.IS_CANARY }}"' + max_retry: 6 + sleep_time: 60 + + - name: Extract endpoint + id: extract-endpoint + shell: bash + run: cd ${{ env.TEST_RESOURCES_FOLDER }}/terraform/java/lambda/lambda && echo API_GATEWAY_URL=$(terraform output -raw api-gateway-url) >> $GITHUB_ENV + + - name: Send request to endpoint + shell: bash + run: sleep 30s; curl -sS ${{ env.API_GATEWAY_URL }} + + - name: Initiate Gradlew Daemon + if: steps.initiate-gradlew == 'failure' + uses: ./.github/workflows/actions/execute_and_retry + continue-on-error: true + with: + command: "./gradlew :validator:build" + cleanup: "./gradlew clean" + max_retry: 3 + sleep_time: 60 + + - name: Validate generated traces + id: trace-validation + # will be removed after data quality bug fixed + continue-on-error: true + run: ./gradlew validator:run --args='-c java/lambda/trace-validation.yml + --testing-id ${{ env.TESTING_ID }} + --endpoint http://${{ env.API_GATEWAY_URL }} + --region ${{ inputs.aws-region }} + --account-id ${{ env.ACCOUNT_ID }} + --metric-namespace ${{ env.METRIC_NAMESPACE }} + --log-group ${{ env.LOG_GROUP_NAME }} + --service-name ${{ env.TERRAFORM_LAMBDA_FUNCTION_NAME }} + --rollup' + + # Validation for pulse telemetry data + - name: Validate generated EMF logs + id: log-validation + if: (success() || steps.trace-validation.outcome == 'failure') && !cancelled() + # will be removed after data quality bug fixed + continue-on-error: true + run: ./gradlew validator:run --args='-c java/lambda/log-validation.yml + --testing-id ${{ env.TESTING_ID }} + --endpoint http://${{ env.API_GATEWAY_URL }} + --region ${{ inputs.aws-region }} + --account-id ${{ env.ACCOUNT_ID }} + --metric-namespace ${{ env.METRIC_NAMESPACE }} + --log-group ${{ env.LOG_GROUP_NAME }} + --service-name ${{ env.TERRAFORM_LAMBDA_FUNCTION_NAME }} + --rollup' + + - name: Validate generated metrics + id: metric-validation + # will be removed after data quality bug fixed + continue-on-error: true + if: (success() || steps.trace-validation.outcome == 'failure' || steps.log-validation.outcome == 'failure') && !cancelled() + run: ./gradlew validator:run --args='-c java/lambda/metric-validation.yml + --testing-id ${{ env.TESTING_ID }} + --endpoint http://${{ env.API_GATEWAY_URL }} + --region ${{ inputs.aws-region }} + --account-id ${{ env.ACCOUNT_ID }} + --metric-namespace ${{ env.METRIC_NAMESPACE }} + --log-group ${{ env.LOG_GROUP_NAME }} + --service-name ${{ env.TERRAFORM_LAMBDA_FUNCTION_NAME }} + --rollup' + + - name: Refresh AWS Credentials + if: ${{ github.event.repository.name == 'aws-application-signals-test-framework' }} + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ env.ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }} + aws-region: ${{ env.E2E_TEST_AWS_REGION }} + + - name: Save test results + if: always() + id: validation-result + run: | + if [ "${{ steps.log-validation.outcome }}" = "success" ] && [ "${{ steps.metric-validation.outcome }}" = "success" ] && [ "${{ steps.trace-validation.outcome }}" = "success" ]; then + echo "validation-result=success" >> $GITHUB_OUTPUT + else + echo "validation-result=failure" >> $GITHUB_OUTPUT + fi + + # Clean up Procedures + - name: Terraform destroy + if: always() + continue-on-error: true + run: | + cd ${{ env.TEST_RESOURCES_FOLDER }}/terraform/java/lambda/lambda && terraform destroy -auto-approve \ + -var="sdk_layer_name=AWSOpenTelemetryDistroJava-${{ github.run_id }}" \ + -var="function_name=${{env.TERRAFORM_LAMBDA_FUNCTION_NAME}}" \ + -var="layer_artifacts_directory=${{ env.ARTIFACTS_DIR }}" \ + -var="region=${{ env.E2E_TEST_AWS_REGION }}" \ + -var="is_canary=${{ env.IS_CANARY }}" diff --git a/terraform/java/lambda/api-gateway-proxy/main.tf b/terraform/java/lambda/api-gateway-proxy/main.tf new file mode 100644 index 00000000..bd6dba5d --- /dev/null +++ b/terraform/java/lambda/api-gateway-proxy/main.tf @@ -0,0 +1,67 @@ +resource "aws_api_gateway_rest_api" "lambda_api_proxy" { + name = var.name +} + +resource "aws_api_gateway_resource" "lambda_api_proxy" { + rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id + parent_id = aws_api_gateway_rest_api.lambda_api_proxy.root_resource_id + path_part = "{proxy+}" +} + +resource "aws_api_gateway_method" "lambda_api_proxy" { + rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id + resource_id = aws_api_gateway_resource.lambda_api_proxy.id + http_method = "ANY" + authorization = "NONE" +} + +resource "aws_api_gateway_integration" "lambda_api" { + rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id + resource_id = aws_api_gateway_method.lambda_api_proxy.resource_id + http_method = aws_api_gateway_method.lambda_api_proxy.http_method + + integration_http_method = "POST" + type = "AWS_PROXY" + uri = var.function_invoke_arn +} + +resource "aws_api_gateway_method" "lambda_api_proxy_root" { + rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id + resource_id = aws_api_gateway_rest_api.lambda_api_proxy.root_resource_id + http_method = "ANY" + authorization = "NONE" +} + +resource "aws_api_gateway_integration" "lambda_api_root" { + rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id + resource_id = aws_api_gateway_method.lambda_api_proxy_root.resource_id + http_method = aws_api_gateway_method.lambda_api_proxy_root.http_method + + integration_http_method = "POST" + type = "AWS_PROXY" + uri = var.function_invoke_arn +} + +resource "aws_api_gateway_deployment" "lambda_api_proxy" { + depends_on = [ + aws_api_gateway_integration.lambda_api, + aws_api_gateway_integration.lambda_api_root, + ] + + rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id +} + +resource "aws_api_gateway_stage" "test" { + stage_name = "default" + rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id + deployment_id = aws_api_gateway_deployment.lambda_api_proxy.id + xray_tracing_enabled = var.enable_xray_tracing +} + +resource "aws_lambda_permission" "lambda_api_allow_gateway" { + action = "lambda:InvokeFunction" + function_name = var.function_name + qualifier = var.function_qualifier + principal = "apigateway.amazonaws.com" + source_arn = "${aws_api_gateway_rest_api.lambda_api_proxy.execution_arn}/*/*" +} diff --git a/terraform/java/lambda/api-gateway-proxy/outputs.tf b/terraform/java/lambda/api-gateway-proxy/outputs.tf new file mode 100644 index 00000000..646329f4 --- /dev/null +++ b/terraform/java/lambda/api-gateway-proxy/outputs.tf @@ -0,0 +1,3 @@ +output "api_gateway_url" { + value = aws_api_gateway_stage.test.invoke_url +} diff --git a/terraform/java/lambda/api-gateway-proxy/variables.tf b/terraform/java/lambda/api-gateway-proxy/variables.tf new file mode 100644 index 00000000..125cd1f2 --- /dev/null +++ b/terraform/java/lambda/api-gateway-proxy/variables.tf @@ -0,0 +1,26 @@ +variable "name" { + type = string + description = "Name of API gateway to create" +} + +variable "function_name" { + type = string + description = "Name of function to proxy to" +} + +variable "function_qualifier" { + type = string + default = null + description = "Qualifier of function to proxy to" +} + +variable "function_invoke_arn" { + type = string + description = "Invoke ARN of function to proxy to" +} + +variable "enable_xray_tracing" { + type = bool + description = "Whether to enable xray tracing of the API gateway" + default = true +} diff --git a/terraform/java/lambda/lambda/layer_arns.tf b/terraform/java/lambda/lambda/layer_arns.tf new file mode 100644 index 00000000..60c818c1 --- /dev/null +++ b/terraform/java/lambda/lambda/layer_arns.tf @@ -0,0 +1,32 @@ +locals { + sdk_layer_arns = { + "af-south-1" = "arn:aws:lambda:af-south-1:904233096616:layer:AWSOpenTelemetryDistroJava:1" + "ap-east-1" = "arn:aws:lambda:ap-east-1:888577020596:layer:AWSOpenTelemetryDistroJava:1" + "ap-northeast-1" = "arn:aws:lambda:ap-northeast-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "ap-northeast-2" = "arn:aws:lambda:ap-northeast-2:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "ap-northeast-3" = "arn:aws:lambda:ap-northeast-3:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "ap-south-1" = "arn:aws:lambda:ap-south-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "ap-south-2" = "arn:aws:lambda:ap-south-2:796973505492:layer:AWSOpenTelemetryDistroJava:1" + "ap-southeast-1" = "arn:aws:lambda:ap-southeast-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "ap-southeast-2" = "arn:aws:lambda:ap-southeast-2:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "ap-southeast-3" = "arn:aws:lambda:ap-southeast-3:039612877180:layer:AWSOpenTelemetryDistroJava:1" + "ap-southeast-4" = "arn:aws:lambda:ap-southeast-4:713881805771:layer:AWSOpenTelemetryDistroJava:1" + "ca-central-1" = "arn:aws:lambda:ca-central-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "eu-central-1" = "arn:aws:lambda:eu-central-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "eu-central-2" = "arn:aws:lambda:eu-central-2:156041407956:layer:AWSOpenTelemetryDistroJava:1" + "eu-north-1" = "arn:aws:lambda:eu-north-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "eu-south-1" = "arn:aws:lambda:eu-south-1:257394471194:layer:AWSOpenTelemetryDistroJava:1" + "eu-south-2" = "arn:aws:lambda:eu-south-2:490004653786:layer:AWSOpenTelemetryDistroJava:1" + "eu-west-1" = "arn:aws:lambda:eu-west-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "eu-west-2" = "arn:aws:lambda:eu-west-2:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "eu-west-3" = "arn:aws:lambda:eu-west-3:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "il-central-1" = "arn:aws:lambda:il-central-1:746669239226:layer:AWSOpenTelemetryDistroJava:1" + "me-central-1" = "arn:aws:lambda:me-central-1:739275441131:layer:AWSOpenTelemetryDistroJava:1" + "me-south-1" = "arn:aws:lambda:me-south-1:980921751758:layer:AWSOpenTelemetryDistroJava:1" + "sa-east-1" = "arn:aws:lambda:sa-east-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "us-east-1" = "arn:aws:lambda:us-east-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "us-east-2" = "arn:aws:lambda:us-east-2:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "us-west-1" = "arn:aws:lambda:us-west-1:615299751070:layer:AWSOpenTelemetryDistroJava:1" + "us-west-2" = "arn:aws:lambda:us-west-2:615299751070:layer:AWSOpenTelemetryDistroJava:1" + } +} diff --git a/terraform/java/lambda/lambda/main.tf b/terraform/java/lambda/lambda/main.tf new file mode 100644 index 00000000..3e7f83d8 --- /dev/null +++ b/terraform/java/lambda/lambda/main.tf @@ -0,0 +1,70 @@ +locals { + architecture = var.architecture == "x86_64" ? "x64" : "arm64" +} + +resource "aws_lambda_layer_version" "sdk_layer" { + count = var.is_canary ? 0 : 1 + layer_name = var.sdk_layer_name + filename = "${var.layer_artifacts_directory}/layer.zip" + compatible_runtimes = ["java17", "java21"] + license_info = "Apache-2.0" + source_code_hash = filebase64sha256("${var.layer_artifacts_directory}/layer.zip") +} + +module "hello-lambda-function" { + source = "terraform-aws-modules/lambda/aws" + version = ">= 2.24.0" + + architectures = compact([var.architecture]) + function_name = var.function_name + handler = "com.amazon.sampleapp.LambdaHandler::handleRequest" + runtime = var.runtime + + create_package = false + local_existing_package = "${var.layer_artifacts_directory}/java-function.zip" + + memory_size = 512 + timeout = 30 + + layers = var.is_canary ? [local.sdk_layer_arns[var.region]] : [aws_lambda_layer_version.sdk_layer[0].arn] + + environment_variables = { + AWS_LAMBDA_EXEC_WRAPPER = "/opt/otel-instrument" + OTEL_SERVICE_NAME = var.function_name + } + + tracing_mode = var.tracing_mode + + attach_policy_statements = true + policy_statements = { + s3 = { + effect = "Allow" + actions = [ + "s3:ListAllMyBuckets", + "s3:ListBucket" + ] + resources = [ + "*" + ] + } + } +} + +module "api-gateway" { + source = "../api-gateway-proxy" + + name = var.function_name + function_name = module.hello-lambda-function.lambda_function_name + function_invoke_arn = module.hello-lambda-function.lambda_function_invoke_arn + enable_xray_tracing = var.tracing_mode == "Active" +} + +resource "aws_iam_role_policy_attachment" "hello-lambda-cloudwatch" { + role = module.hello-lambda-function.lambda_function_name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" +} + +resource "aws_iam_role_policy_attachment" "test_xray" { + role = module.hello-lambda-function.lambda_function_name + policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess" +} \ No newline at end of file diff --git a/terraform/java/lambda/lambda/outputs.tf b/terraform/java/lambda/lambda/outputs.tf new file mode 100644 index 00000000..5de8044c --- /dev/null +++ b/terraform/java/lambda/lambda/outputs.tf @@ -0,0 +1,7 @@ +output "api-gateway-url" { + value = module.api-gateway.api_gateway_url +} + +output "function_role_name" { + value = module.hello-lambda-function.lambda_role_name +} diff --git a/terraform/java/lambda/lambda/variables.tf b/terraform/java/lambda/lambda/variables.tf new file mode 100644 index 00000000..eb8cbf91 --- /dev/null +++ b/terraform/java/lambda/lambda/variables.tf @@ -0,0 +1,47 @@ +variable "function_name" { + type = string + description = "Name of sample app function / API gateway" + default = "aws-opentelemetry-distro-java" +} + +variable "sdk_layer_name" { + type = string + description = "Name of published SDK layer" + default = "AWSOpenTelemetryDistroJava" +} + +variable "tracing_mode" { + type = string + description = "Lambda function tracing mode" + default = "Active" +} + +variable "runtime" { + type = string + description = "Java runtime version used for sample Lambda Function" + default = "java17" +} + +variable "architecture" { + type = string + description = "Lambda function architecture, valid values are arm64 or x86_64" + default = "x86_64" +} + +variable "region" { + type = string + description = "Lambda function running region, default value is us-west-2" + default = "us-west-2" +} + +variable "is_canary" { + type = bool + default = false + description = "Whether to create the resource or not" +} + +variable "layer_artifacts_directory" { + type = string + default = "./layer_artifacts" + description = "Lambda layer and function artifacts directory" +} diff --git a/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java b/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java index 7d7e1a4f..9d7ff14d 100644 --- a/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java +++ b/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java @@ -393,6 +393,13 @@ public enum PredefinedExpectedTemplate implements FileConfig { DOTNET_LAMBDA_INVOKE_TRACE("/expected-data-template/dotnet/lambda/lambda-invoke-trace.mustache"), DOTNET_LAMBDA_AWS_SDK_CALL_LOG("/expected-data-template/dotnet/lambda/aws-sdk-call-log.mustache"), DOTNET_LAMBDA_AWS_SDK_CALL_METRIC("/expected-data-template/dotnet/lambda/aws-sdk-call-metric.mustache"), + + /** Java Lambda Test Case Validations */ + JAVA_LAMBDA_INVOKE_LOG("/expected-data-template/java/lambda/lambda-invoke-log.mustache"), + JAVA_LAMBDA_INVOKE_METRIC("/expected-data-template/java/lambda/lambda-invoke-metric.mustache"), + JAVA_LAMBDA_INVOKE_TRACE("/expected-data-template/java/lambda/lambda-invoke-trace.mustache"), + JAVA_LAMBDA_AWS_SDK_CALL_LOG("/expected-data-template/java/lambda/aws-sdk-call-log.mustache"), + JAVA_LAMBDA_AWS_SDK_CALL_METRIC("/expected-data-template/java/lambda/aws-sdk-call-metric.mustache"), ; private String path; diff --git a/validator/src/main/resources/expected-data-template/java/lambda/aws-sdk-call-log.mustache b/validator/src/main/resources/expected-data-template/java/lambda/aws-sdk-call-log.mustache new file mode 100644 index 00000000..1d5d3c44 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/java/lambda/aws-sdk-call-log.mustache @@ -0,0 +1,9 @@ +[{ + "Environment": "^lambda:default$", + "PlatformType": "^AWS::Lambda$", + "Operation": "{{serviceName}}/FunctionHandler", + "Service": "^{{serviceName}}$", + "RemoteService": "AWS::S3", + "RemoteOperation": "ListBuckets", + "Lambda.Function.Name": "^{{serviceName}}$" +}] \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/java/lambda/aws-sdk-call-metric.mustache b/validator/src/main/resources/expected-data-template/java/lambda/aws-sdk-call-metric.mustache new file mode 100644 index 00000000..ced234c2 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/java/lambda/aws-sdk-call-metric.mustache @@ -0,0 +1,227 @@ +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: {{serviceName}}/FunctionHandler + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: {{serviceName}}/FunctionHandler + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + - + name: RemoteOperation + value: ListBuckets + - + name: RemoteService + value: AWS::S3 + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + - + name: RemoteService + value: AWS::S3 + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + - + name: RemoteOperation + value: ListBuckets + - + name: RemoteService + value: AWS::S3 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: {{serviceName}}/FunctionHandler + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: {{serviceName}}/FunctionHandler + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + - + name: RemoteOperation + value: ListBuckets + - + name: RemoteService + value: AWS::S3 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + - + name: RemoteService + value: AWS::S3 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + - + name: RemoteOperation + value: ListBuckets + - + name: RemoteService + value: AWS::S3 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: {{serviceName}}/FunctionHandler + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: {{serviceName}}/FunctionHandler + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + - + name: RemoteOperation + value: ListBuckets + - + name: RemoteService + value: AWS::S3 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + - + name: RemoteService + value: AWS::S3 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + - + name: RemoteOperation + value: ListBuckets + - + name: RemoteService + value: AWS::S3 diff --git a/validator/src/main/resources/expected-data-template/java/lambda/lambda-invoke-log.mustache b/validator/src/main/resources/expected-data-template/java/lambda/lambda-invoke-log.mustache new file mode 100644 index 00000000..657631c5 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/java/lambda/lambda-invoke-log.mustache @@ -0,0 +1,7 @@ +[{ + "Environment": "^lambda:default$", + "Operation": "{{serviceName}}/FunctionHandler", + "PlatformType": "^AWS::Lambda$", + "Service": "^{{serviceName}}$", + "Lambda.Function.Name": "^{{serviceName}}$" +}] \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/java/lambda/lambda-invoke-metric.mustache b/validator/src/main/resources/expected-data-template/java/lambda/lambda-invoke-metric.mustache new file mode 100644 index 00000000..ca4d5042 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/java/lambda/lambda-invoke-metric.mustache @@ -0,0 +1,74 @@ +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: {{serviceName}}/FunctionHandler + - + name: Environment + value: lambda:default + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: {{serviceName}}/FunctionHandler + - + name: Environment + value: lambda:default + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: lambda:default + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: {{serviceName}}/FunctionHandler + - + name: Environment + value: lambda:default diff --git a/validator/src/main/resources/expected-data-template/java/lambda/lambda-invoke-trace.mustache b/validator/src/main/resources/expected-data-template/java/lambda/lambda-invoke-trace.mustache new file mode 100644 index 00000000..811dba23 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/java/lambda/lambda-invoke-trace.mustache @@ -0,0 +1,12 @@ +[{ + "name": "^{{serviceName}}/default$", + "subsegments": [ + { + "name": "^Lambda$", + "aws": { + "function_name": "^{{serviceName}}$" + }, + "namespace": "^aws$" + } + ] +}] \ No newline at end of file diff --git a/validator/src/main/resources/validations/java/lambda/log-validation.yml b/validator/src/main/resources/validations/java/lambda/log-validation.yml new file mode 100644 index 00000000..db7e7caf --- /dev/null +++ b/validator/src/main/resources/validations/java/lambda/log-validation.yml @@ -0,0 +1,10 @@ +- + validationType: "cw-log" + httpPath: "/lambda-invoke" + callingType: "lambda-invoke" + expectedLogStructureTemplate: "JAVA_LAMBDA_INVOKE_LOG" +- + validationType: "cw-log" + httpPath: "/lambda-aws-sdk" + callingType: "lambda-aws-sdk" + expectedLogStructureTemplate: "JAVA_LAMBDA_AWS_SDK_CALL_LOG" \ No newline at end of file diff --git a/validator/src/main/resources/validations/java/lambda/metric-validation.yml b/validator/src/main/resources/validations/java/lambda/metric-validation.yml new file mode 100644 index 00000000..56bfa83a --- /dev/null +++ b/validator/src/main/resources/validations/java/lambda/metric-validation.yml @@ -0,0 +1,11 @@ +- + validationType: "cw-metric" + httpPath: "/lambda-invoke" + callingType: "lambda-invoke" + expectedMetricTemplate: "JAVA_LAMBDA_INVOKE_METRIC" + +- + validationType: "cw-metric" + httpPath: "/lambda-aws-sdk" + callingType: "lambda-aws-sdk" + expectedMetricTemplate: "JAVA_LAMBDA_AWS_SDK_CALL_METRIC" \ No newline at end of file diff --git a/validator/src/main/resources/validations/java/lambda/trace-validation.yml b/validator/src/main/resources/validations/java/lambda/trace-validation.yml new file mode 100644 index 00000000..593788f1 --- /dev/null +++ b/validator/src/main/resources/validations/java/lambda/trace-validation.yml @@ -0,0 +1,5 @@ +- + validationType: "trace" + httpPath: "/lambda-invoke" + callingType: "lambda-invoke" + expectedTraceTemplate: "JAVA_LAMBDA_INVOKE_TRACE"