diff --git a/.github/workflows/dotnet-k8s-canary.yml b/.github/workflows/dotnet-k8s-canary.yml new file mode 100644 index 000000000..3a19463a6 --- /dev/null +++ b/.github/workflows/dotnet-k8s-canary.yml @@ -0,0 +1,26 @@ +## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +## SPDX-License-Identifier: Apache-2.0 + +## This workflow aims to run the Application Signals end-to-end tests as a canary to +## test the artifacts for App Signals enablement. It will deploy the CloudWatch Agent +## Operator and our sample app and remote service onto a native K8s cluster, call the +## APIs, and validate the generated telemetry, including logs, metrics, and traces. +## It will then clean up the cluster and EC2 instance it runs on for the next test run. +name: Dotnet K8s Enablement Canary Testing +on: + schedule: + - cron: '*/15 * * * *' # run the workflow every 15 minutes + workflow_dispatch: # be able to run the workflow on demand + +permissions: + id-token: write + contents: read + +jobs: + k8s: + uses: ./.github/workflows/dotnet-k8s-retry.yml + secrets: inherit + with: + # To run in more regions, a cluster must be provisioned manually on EC2 instances in that region + aws-region: 'us-east-1' + caller-workflow-name: 'appsignals-e2e-dotnet-k8s-canary-test' \ No newline at end of file diff --git a/.github/workflows/dotnet-k8s-retry.yml b/.github/workflows/dotnet-k8s-retry.yml new file mode 100644 index 000000000..c81b2efd2 --- /dev/null +++ b/.github/workflows/dotnet-k8s-retry.yml @@ -0,0 +1,61 @@ +## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +## SPDX-License-Identifier: Apache-2.0 + +# This is a reusable workflow for running the Enablement test for App Signals. +# 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: Dotnet K8s Retry +on: + workflow_call: + inputs: + aws-region: + required: true + type: string + caller-workflow-name: + required: true + type: string + +concurrency: + group: 'dotnet-k8s-${{ inputs.aws-region }}-${{ github.ref_name }}' + cancel-in-progress: false + +permissions: + id-token: write + contents: read + +jobs: + dotnet-k8s-attempt-1: + uses: ./.github/workflows/dotnet-k8s-test.yml + secrets: inherit + with: + aws-region: ${{ inputs.aws-region }} + caller-workflow-name: ${{ inputs.caller-workflow-name }} + + dotnet-k8s-attempt-2: + needs: [ dotnet-k8s-attempt-1 ] + if: ${{ needs.dotnet-k8s-attempt-1.outputs.job-started != 'true' }} + uses: ./.github/workflows/dotnet-k8s-test.yml + secrets: inherit + with: + aws-region: ${{ inputs.aws-region }} + caller-workflow-name: ${{ inputs.caller-workflow-name }} + + publish-metric-attempt-1: + needs: [ dotnet-k8s-attempt-1, dotnet-k8s-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.dotnet-k8s-attempt-1.outputs.validation-result || needs.dotnet-k8s-attempt-2.outputs.validation-result }} + + publish-metric-attempt-2: + needs: [ dotnet-k8s-attempt-1, dotnet-k8s-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.dotnet-k8s-attempt-1.outputs.validation-result || needs.dotnet-k8s-attempt-2.outputs.validation-result }} \ No newline at end of file diff --git a/.github/workflows/dotnet-k8s-test.yml b/.github/workflows/dotnet-k8s-test.yml new file mode 100644 index 000000000..91815ab74 --- /dev/null +++ b/.github/workflows/dotnet-k8s-test.yml @@ -0,0 +1,279 @@ +## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +## SPDX-License-Identifier: Apache-2.0 + +# This is a reusable workflow for running the Enablement test for App Signals. +# 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: Dotnet K8s on EC2 Use Case +on: + workflow_call: + inputs: + aws-region: + required: true + type: string + caller-workflow-name: + required: true + type: string + caller-repository: + required: false + type: string + adot-image-name: + required: false + type: string + cw-agent-operator-tag: + required: false + type: string + outputs: + job-started: + value: ${{ jobs.dotnet-k8s.outputs.job-started }} + validation-result: + value: ${{ jobs.dotnet-k8s.outputs.validation-result }} + +permissions: + id-token: write + contents: read + +env: + E2E_TEST_AWS_REGION: ${{ inputs.aws-region }} + CALLER_WORKFLOW_NAME: ${{ inputs.caller-workflow-name }} + CALLER_REPOSITORY: ${{ inputs.caller-repository }} + ADOT_IMAGE_NAME: ${{ inputs.adot-image-name }} + CW_AGENT_OPERATOR_TAG: ${{ inputs.cw-agent-operator-tag }} + E2E_TEST_ACCOUNT_ID: ${{ secrets.APPLICATION_SIGNALS_E2E_TEST_ACCOUNT_ID }} + E2E_TEST_ROLE_NAME: ${{ secrets.APPLICATION_SIGNALS_E2E_TEST_ROLE_NAME }} + SAMPLE_APP_NAMESPACE: dotnet-sample-app-namespace + METRIC_NAMESPACE: ApplicationSignals + LOG_GROUP_NAME: /aws/application-signals/data + TEST_RESOURCES_FOLDER: ${GITHUB_WORKSPACE} + +jobs: + dotnet-k8s: + 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 + + - name: Generate testing id + run: echo TESTING_ID="${{ github.run_id }}-${{ github.run_number }}-${RANDOM}" >> $GITHUB_ENV + + - 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: 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 Secrets + uses: aws-actions/aws-secretsmanager-get-secrets@v1 + with: + secret-ids: | + ACCOUNT_ID, region-account/${{ env.E2E_TEST_AWS_REGION }} + DOTNET_MAIN_SAMPLE_APP_IMAGE, e2e-test/dotnet-main-sample-app-image + DOTNET_REMOTE_SAMPLE_APP_IMAGE, e2e-test/dotnet-remote-sample-app-image + RELEASE_TESTING_ECR_ACCOUNT, e2e-test/${{ github.event.repository.name }}/dotnet-k8s-release-testing-account + + - name: Retrieve K8s EC2 Secrets + if: ${{ env.CALLER_WORKFLOW_NAME != 'k8s-os-patching' }} + uses: aws-actions/aws-secretsmanager-get-secrets@v1 + with: + secret-ids: | + MAIN_SERVICE_ENDPOINT, e2e-test/${{ github.event.repository.name }}/dotnet-k8s-master-node-endpoint + MASTER_NODE_SSH_KEY, e2e-test/${{ github.event.repository.name }}/dotnet-k8s-ssh-key + + - name: Retrieve K8s EC2 OS Patching Secrets + if: ${{ env.CALLER_WORKFLOW_NAME == 'k8s-os-patching' }} + uses: aws-actions/aws-secretsmanager-get-secrets@v1 + with: + secret-ids: | + MAIN_SERVICE_ENDPOINT, e2e-test/${{ env.CALLER_REPOSITORY }}/dotnet-k8s-master-node-endpoint-pending-value + MASTER_NODE_SSH_KEY, e2e-test/${{ env.CALLER_REPOSITORY }}/dotnet-k8s-ssh-key-pending-value + + - name: Prepare and upload sample app deployment files + working-directory: terraform/dotnet/k8s/deploy/resources + run: | + sed -i 's#\${TESTING_ID}#${{ env.TESTING_ID }}#' dotnet-frontend-service-depl.yaml + sed -i 's#\${IMAGE}#${{ env.ACCOUNT_ID }}.dkr.ecr.${{ env.E2E_TEST_AWS_REGION }}.amazonaws.com/${{ env.DOTNET_MAIN_SAMPLE_APP_IMAGE }}#' dotnet-frontend-service-depl.yaml + sed -i 's#\${TESTING_ID}#${{ env.TESTING_ID }}#' dotnet-remote-service-depl.yaml + sed -i 's#\${IMAGE}#${{ env.ACCOUNT_ID }}.dkr.ecr.${{ env.E2E_TEST_AWS_REGION }}.amazonaws.com/${{ env.DOTNET_REMOTE_SAMPLE_APP_IMAGE }}#' dotnet-remote-service-depl.yaml + aws s3api put-object --bucket aws-appsignals-sample-app-prod-${{ env.E2E_TEST_AWS_REGION }} --key dotnet-frontend-service-depl-${{ env.TESTING_ID }}.yaml --body dotnet-frontend-service-depl.yaml + aws s3api put-object --bucket aws-appsignals-sample-app-prod-${{ env.E2E_TEST_AWS_REGION }} --key dotnet-remote-service-depl-${{ env.TESTING_ID }}.yaml --body dotnet-remote-service-depl.yaml + + - 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/dotnet/k8s/deploy && terraform init && terraform validate" + cleanup: "rm -rf .terraform && rm -rf .terraform.lock.hcl" + max_retry: 6 + sleep_time: 60 + + - name: Get ECR to Patch + run: | + if [ "${{ github.event.repository.name }}" = "amazon-cloudwatch-agent" ]; then + echo PATCH_IMAGE_ARN="${{ secrets.AWS_ECR_PRIVATE_REGISTRY }}/cwagent-integration-test:${{ github.sha }}" >> $GITHUB_ENV + elif [ "${{ github.event.repository.name }}" = "amazon-cloudwatch-agent-operator" ]; then + echo PATCH_IMAGE_ARN="${{ vars.ECR_OPERATOR_STAGING_REPO }}:${{ env.CW_AGENT_OPERATOR_TAG }}" >> $GITHUB_ENV + elif [ "${{ github.event.repository.name }}" = "aws-otel-dotnet-instrumentation" ]; then + echo PATCH_IMAGE_ARN="${{ env.ADOT_IMAGE_NAME }}" >> $GITHUB_ENV + fi + + - name: Deploy Operator and Sample App using Terraform + working-directory: terraform/dotnet/k8s/deploy + run: | + terraform apply -auto-approve \ + -var="aws_region=${{ env.E2E_TEST_AWS_REGION }}" \ + -var="test_id=${{ env.TESTING_ID }}" \ + -var="ssh_key=${{ env.MASTER_NODE_SSH_KEY }}" \ + -var="host=${{ env.MAIN_SERVICE_ENDPOINT }}" \ + -var="repository=${{ github.event.repository.name }}" \ + -var="patch_image_arn=${{ env.PATCH_IMAGE_ARN }}" \ + -var="release_testing_ecr_account=${{ env.RELEASE_TESTING_ECR_ACCOUNT }}" + + - name: Get Main and Remote Service IP + run: | + echo MAIN_SERVICE_IP="$(aws ssm get-parameter --region ${{ env.E2E_TEST_AWS_REGION }} --name dotnet-main-service-ip-${{ env.TESTING_ID }} | jq -r '.Parameter.Value')" >> $GITHUB_ENV + echo REMOTE_SERVICE_IP="$(aws ssm get-parameter --region us-east-1 --name dotnet-remote-service-ip-${{ env.TESTING_ID }} | jq -r '.Parameter.Value')" >> $GITHUB_ENV + + - 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 + + # Validation for pulse telemetry data + - name: Validate generated EMF logs + id: log-validation + run: ./gradlew validator:run --args='-c dotnet/k8s/log-validation.yml + --testing-id ${{ env.TESTING_ID }} + --endpoint http://${{ env.MAIN_SERVICE_IP }}:8080 + --region ${{ env.E2E_TEST_AWS_REGION }} + --account-id ${{ env.ACCOUNT_ID }} + --metric-namespace ${{ env.METRIC_NAMESPACE }} + --log-group ${{ env.LOG_GROUP_NAME }} + --platform-info k8s-cluster-${{ env.TESTING_ID }} + --app-namespace ${{ env.SAMPLE_APP_NAMESPACE }} + --service-name dotnet-sample-app-deployment-${{ env.TESTING_ID }} + --remote-service-name dotnet-sample-r-app-deployment-${{ env.TESTING_ID }} + --remote-service-deployment-name dotnet-sample-r-app-deployment-${{ env.TESTING_ID }} + --query-string ip=${{ env.REMOTE_SERVICE_IP }}&testingId=${{ env.TESTING_ID }} + --rollup' + + - name: Validate generated metrics + id: metric-validation + if: (success() || steps.log-validation.outcome == 'failure') && !cancelled() + run: ./gradlew validator:run --args='-c dotnet/k8s/metric-validation.yml + --testing-id ${{ env.TESTING_ID }} + --endpoint http://${{ env.MAIN_SERVICE_IP }}:8080 + --region ${{ env.E2E_TEST_AWS_REGION }} + --account-id ${{ env.ACCOUNT_ID }} + --metric-namespace ${{ env.METRIC_NAMESPACE }} + --log-group ${{ env.LOG_GROUP_NAME }} + --platform-info k8s-cluster-${{ env.TESTING_ID }} + --app-namespace ${{ env.SAMPLE_APP_NAMESPACE }} + --service-name dotnet-sample-app-deployment-${{ env.TESTING_ID }} + --remote-service-name dotnet-sample-r-app-deployment-${{ env.TESTING_ID }} + --remote-service-deployment-name dotnet-sample-r-app-deployment-${{ env.TESTING_ID }} + --query-string ip=${{ env.REMOTE_SERVICE_IP }}&testingId=${{ env.TESTING_ID }} + --rollup' + + - name: Validate generated traces + id: trace-validation + if: (success() || steps.log-validation.outcome == 'failure' || steps.metric-validation.outcome == 'failure') && !cancelled() + run: ./gradlew validator:run --args='-c dotnet/k8s/trace-validation.yml + --testing-id ${{ env.TESTING_ID }} + --endpoint http://${{ env.MAIN_SERVICE_IP }}:8080 + --region ${{ env.E2E_TEST_AWS_REGION }} + --account-id ${{ env.ACCOUNT_ID }} + --metric-namespace ${{ env.METRIC_NAMESPACE }} + --log-group ${{ env.LOG_GROUP_NAME }} + --platform-info k8s-cluster-${{ env.TESTING_ID }} + --app-namespace ${{ env.SAMPLE_APP_NAMESPACE }} + --service-name dotnet-sample-app-deployment-${{ env.TESTING_ID }} + --remote-service-name dotnet-sample-r-app-deployment-${{ env.TESTING_ID }} + --remote-service-deployment-name dotnet-sample-r-app-deployment-${{ env.TESTING_ID }} + --query-string ip=${{ env.REMOTE_SERVICE_IP }}&testingId=${{ env.TESTING_ID }} + --rollup' + + - 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: Initiate Terraform for Cleanup + if: always() + uses: ./.github/workflows/actions/execute_and_retry + with: + command: "cd ${{ env.TEST_RESOURCES_FOLDER }}/terraform/dotnet/k8s/cleanup && terraform init && terraform validate" + cleanup: "rm -rf .terraform && rm -rf .terraform.lock.hcl" + + - name: Clean Up Operator and Sample App using Terraform + if: always() + working-directory: terraform/dotnet/k8s/cleanup + run: | + terraform apply -auto-approve \ + -var="aws_region=${{ env.E2E_TEST_AWS_REGION }}" \ + -var="test_id=${{ env.TESTING_ID }}" \ + -var="ssh_key=${{ env.MASTER_NODE_SSH_KEY }}" \ + -var="host=${{ env.MAIN_SERVICE_ENDPOINT }}" + + - name: Terraform destroy - deployment + if: always() + continue-on-error: true + working-directory: terraform/dotnet/k8s/deploy + run: | + terraform destroy -auto-approve \ + -var="test_id=${{ env.TESTING_ID }}" + + - name: Terraform destroy - cleanup + if: always() + continue-on-error: true + working-directory: terraform/dotnet/k8s/cleanup + run: | + terraform destroy -auto-approve \ + -var="test_id=${{ env.TESTING_ID }}" + + - name: Delete deployment files + if: always() + continue-on-error: true + run: | + aws s3api delete-object --bucket aws-appsignals-sample-app-prod-${{ env.E2E_TEST_AWS_REGION }} --key dotnet-frontend-service-depl-${{ env.TESTING_ID }}.yaml + aws s3api delete-object --bucket aws-appsignals-sample-app-prod-${{ env.E2E_TEST_AWS_REGION }} --key dotnet-remote-service-depl-${{ env.TESTING_ID }}.yaml diff --git a/terraform/dotnet/k8s/cleanup/main.tf b/terraform/dotnet/k8s/cleanup/main.tf new file mode 100644 index 000000000..2d78b3597 --- /dev/null +++ b/terraform/dotnet/k8s/cleanup/main.tf @@ -0,0 +1,56 @@ +# ------------------------------------------------------------------------ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +# ------------------------------------------------------------------------- + +resource "null_resource" "cleanup" { + connection { + type = "ssh" + user = var.user + private_key = var.ssh_key + host = var.host + } + + + provisioner "remote-exec" { + inline = [ + <<-EOF + # Allow terraform to fail any of the following steps without exiting + set +e + + # Uninstall the operator and remove the repo from the EC2 instance + echo "LOG: Uninstalling CloudWatch Agent Operator" + helm uninstall --debug --namespace amazon-cloudwatch amazon-cloudwatch-operator --ignore-not-found + echo "LOG: Deleting Helm Charts repo from environment" + [ ! -e helm-charts ] || sudo rm -r helm-charts + + # Delete sample app resources + echo "LOG: Deleting sample app namespace" + kubectl delete namespace dotnet-sample-app-namespace + echo "LOG: Deleting sample app deployment files" + [ ! -e dotnet-frontend-service-depl.yaml ] || rm dotnet-frontend-service-depl.yaml + [ ! -e dotnet-remote-service-depl.yaml ] || rm dotnet-remote-service-depl.yaml + sleep 10 + + # Print cluster state when done clean up procedures + echo "LOG: Printing cluster state after cleanup" + kubectl get pods -A + + # Delete ssm parameter for main and remote service ip + aws ssm delete-parameter --name dotnet-main-service-ip-${var.test_id} + aws ssm delete-parameter --name dotnet-remote-service-ip-${var.test_id} + + EOF + ] + } +} \ No newline at end of file diff --git a/terraform/dotnet/k8s/cleanup/variables.tf b/terraform/dotnet/k8s/cleanup/variables.tf new file mode 100644 index 000000000..0771f1737 --- /dev/null +++ b/terraform/dotnet/k8s/cleanup/variables.tf @@ -0,0 +1,36 @@ +# ------------------------------------------------------------------------ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +# ------------------------------------------------------------------------- + +variable "test_id" { + default = "dummy-123" +} + +variable "aws_region" { + default = "" +} + +variable "user" { + default = "ec2-user" +} + +variable "ssh_key" { + default = "" + description = "This variable is responsible for providing the SSH key of the master node to allow terraform to interact with the cluster" +} + +variable "host" { + default = "" + description = "This variable is responsible for defining which host (ec2 instance) we connect to for the K8s-on-EC2 test" +} \ No newline at end of file diff --git a/terraform/dotnet/k8s/deploy/main.tf b/terraform/dotnet/k8s/deploy/main.tf new file mode 100644 index 000000000..277c7a49e --- /dev/null +++ b/terraform/dotnet/k8s/deploy/main.tf @@ -0,0 +1,172 @@ +# ------------------------------------------------------------------------ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +# ------------------------------------------------------------------------- + +resource "null_resource" "deploy" { + connection { + type = "ssh" + user = var.user + private_key = var.ssh_key + host = var.host + } + + provisioner "remote-exec" { + inline = [ + <<-EOF + + # Make the Terraform fail if any step throws an error + set -e + + # Ensure environment is clean + echo "LOG: Rerunning cleanup commands in case of cleanup failure in previous run" + helm uninstall --debug --namespace amazon-cloudwatch amazon-cloudwatch-operator --ignore-not-found + kubectl delete namespace dotnet-sample-app-namespace --ignore-not-found=true + [ ! -e helm-charts ] || sudo rm -r helm-charts + [ ! -e dotnet-frontend-service-depl.yaml ] || rm dotnet-frontend-service-depl.yaml + [ ! -e dotnet-remote-service-depl.yaml ] || rm dotnet-remote-service-depl.yaml + + echo "LOG: Getting latest helm chart release URL" + latest_version_url=$(curl -s https://api.github.com/repos/aws-observability/helm-charts/releases/latest | grep "tarball_url" | cut -d '"' -f 4) + echo "LOG: The latest helm chart version url is $latest_version_url" + echo "LOG: Downloading and unpacking the helm chart repo" + curl -L $latest_version_url -o aws-observability-helm-charts-latest.tar.gz + mkdir helm-charts + tar -xvzf aws-observability-helm-charts-latest.tar.gz -C helm-charts + cd helm-charts/aws-observability-helm-charts*/charts/amazon-cloudwatch-observability + + echo "LOG: Installing CloudWatch Agent Operator using Helm" + helm upgrade --install --debug --namespace amazon-cloudwatch amazon-cloudwatch-operator ./ --create-namespace --set region=${var.aws_region} --set clusterName=k8s-cluster-${var.test_id} + + # Wait for pods to exist before checking if they're ready + sleep 60 + kubectl wait --for=condition=Ready pods --all --selector=app.kubernetes.io/name=amazon-cloudwatch-observability -n amazon-cloudwatch --timeout=60s + kubectl wait --for=condition=Ready pods --all --selector=app.kubernetes.io/name=cloudwatch-agent -n amazon-cloudwatch --timeout=60s + + if [ "${var.repository}" = "amazon-cloudwatch-agent" ]; then + RELEASE_TESTING_SECRET_NAME=release-testing-ecr-secret + RELEASE_TESTING_TOKEN=`aws ecr --region=us-west-2 get-authorization-token --output text --query authorizationData[].authorizationToken | base64 -d | cut -d: -f2` + kubectl delete secret -n amazon-cloudwatch --ignore-not-found $RELEASE_TESTING_SECRET_NAME + kubectl create secret -n amazon-cloudwatch docker-registry $RELEASE_TESTING_SECRET_NAME \ + --docker-server=https://${var.release_testing_ecr_account}.dkr.ecr.us-west-2.amazonaws.com \ + --docker-username=AWS \ + --docker-password="$${RELEASE_TESTING_TOKEN}" + + kubectl patch serviceaccount cloudwatch-agent -n amazon-cloudwatch -p='{"imagePullSecrets": [{"name": "release-testing-ecr-secret"}]}' + kubectl delete pods --all -n amazon-cloudwatch + elif [ "${var.repository}" = "amazon-cloudwatch-agent-operator" ]; then + RELEASE_TESTING_SECRET_NAME=release-testing-ecr-secret + RELEASE_TESTING_TOKEN=`aws ecr --region=us-west-2 get-authorization-token --output text --query authorizationData[].authorizationToken | base64 -d | cut -d: -f2` + kubectl delete secret -n amazon-cloudwatch --ignore-not-found $RELEASE_TESTING_SECRET_NAME + kubectl create secret -n amazon-cloudwatch docker-registry $RELEASE_TESTING_SECRET_NAME \ + --docker-server=https://${var.release_testing_ecr_account}.dkr.ecr.us-west-2.amazonaws.com \ + --docker-username=AWS \ + --docker-password="$${RELEASE_TESTING_TOKEN}" + + kubectl patch deploy -n amazon-cloudwatch amazon-cloudwatch-observability-controller-manager --type='json' -p='[{"op": "add", "path": "/spec/template/spec/imagePullSecrets", "value": [{"name": "release-testing-ecr-secret"}]}]' + kubectl delete pods --all -n amazon-cloudwatch + fi + + if [ "${var.repository}" = "amazon-cloudwatch-agent-operator" ]; then + kubectl patch deploy -n amazon-cloudwatch amazon-cloudwatch-observability-controller-manager --type='json' -p '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "${var.patch_image_arn}"}, {"op": "replace", "path": "/spec/template/spec/containers/0/imagePullPolicy", "value": "Always"}]]' + kubectl delete pods --all -n amazon-cloudwatch + sleep 10 + kubectl wait --for=condition=Ready pod --all -n amazon-cloudwatch + elif [ "${var.repository}" = "amazon-cloudwatch-agent" ]; then + kubectl patch amazoncloudwatchagents -n amazon-cloudwatch cloudwatch-agent --type='json' -p='[{"op": "replace", "path": "/spec/image", "value": ${var.patch_image_arn}}]' + kubectl delete pods --all -n amazon-cloudwatch + sleep 10 + kubectl wait --for=condition=Ready pod --all -n amazon-cloudwatch + elif [ "${var.repository}" = "aws-otel-dotnet-instrumentation" ]; then + kubectl patch deploy -n amazon-cloudwatch amazon-cloudwatch-observability-controller-manager --type='json' \ + -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/args/4", "value": "--auto-instrumentation-dotnet-image=${var.patch_image_arn}"}]' + kubectl delete pods --all -n amazon-cloudwatch + sleep 10 + kubectl wait --for=condition=Ready pod --all -n amazon-cloudwatch + fi + + # Create sample app namespace + echo "LOG: Creating sample app namespace" + kubectl create namespace dotnet-sample-app-namespace + + # Set up secret to pull image with + echo "LOG: Creating secret to access ECR images" + ACCOUNT=$(aws sts get-caller-identity --query 'Account' --output text) + SECRET_NAME=ecr-secret + TOKEN=`aws ecr --region=${var.aws_region} get-authorization-token --output text --query authorizationData[].authorizationToken | base64 -d | cut -d: -f2` + + echo "LOG: Deleting secret if it exists" + kubectl delete secret -n dotnet-sample-app-namespace --ignore-not-found $SECRET_NAME + + echo "LOG: Creating secret for pulling sample app ECR" + kubectl create secret -n dotnet-sample-app-namespace docker-registry $SECRET_NAME \ + --docker-server=https://$ACCOUNT.dkr.ecr.${var.aws_region}.amazonaws.com \ + --docker-username=AWS \ + --docker-password="$${TOKEN}" + + # Deploy sample app + echo "LOG: Pulling sample app deployment files" + # cd to ensure everything is downloaded into root directory so cleanup is each + cd ~ + aws s3api get-object --bucket aws-appsignals-sample-app-prod-us-east-1 --key dotnet-frontend-service-depl-${var.test_id}.yaml dotnet-frontend-service-depl.yaml + aws s3api get-object --bucket aws-appsignals-sample-app-prod-us-east-1 --key dotnet-remote-service-depl-${var.test_id}.yaml dotnet-remote-service-depl.yaml + + # Patch the staging image if this is running as part of release testing + if [ "${var.repository}" = "aws-otel-dotnet-instrumentation" ]; then + RELEASE_TESTING_SECRET_NAME=release-testing-ecr-secret + kubectl delete secret -n dotnet-sample-app-namespace --ignore-not-found $RELEASE_TESTING_SECRET_NAME + kubectl create secret -n dotnet-sample-app-namespace docker-registry $RELEASE_TESTING_SECRET_NAME \ + --docker-server=https://${var.release_testing_ecr_account}.dkr.ecr.us-east-1.amazonaws.com \ + --docker-username=AWS \ + --docker-password="$${TOKEN}" + + yq eval '.spec.template.spec.imagePullSecrets += [{"name": "release-testing-ecr-secret"}]' -i dotnet-frontend-service-depl.yaml + yq eval '.spec.template.spec.imagePullSecrets += [{"name": "release-testing-ecr-secret"}]' -i dotnet-remote-service-depl.yaml + fi + + echo "LOG: Applying sample app deployment files" + kubectl apply -f dotnet-frontend-service-depl.yaml + kubectl apply -f dotnet-remote-service-depl.yaml + + echo "Wait for sample app to be reach ready state" + sleep 10 + kubectl wait --for=condition=Ready --request-timeout '10m' pod --all -n dotnet-sample-app-namespace + + # Emit main and remote service pod IP + echo "LOG: Outputting remote service pod IP to SSM using put-parameter API" + aws ssm put-parameter --region ${var.aws_region} --name dotnet-main-service-ip-${var.test_id} --type String --overwrite --value $(kubectl get pod --selector=app=dotnet-sample-app -n dotnet-sample-app-namespace -o jsonpath='{.items[0].status.podIP}') + aws ssm put-parameter --region ${var.aws_region} --name dotnet-remote-service-ip-${var.test_id} --type String --overwrite --value $(kubectl get pod --selector=app=dotnet-remote-app -n dotnet-sample-app-namespace -o jsonpath='{.items[0].status.podIP}') + + # Wait a bit more in case the sample apps aren't ready yet + sleep 30 + + # Deploy the traffic generator + kubectl create deployment -n dotnet-sample-app-namespace traffic-generator \ + --image=$ACCOUNT.dkr.ecr.${var.aws_region}.amazonaws.com/e2e-test-resource:traffic-generator \ + --replicas=1 + + # Patch it with ImagePull always policy so that it pulls the latest image from the ECR + kubectl patch deployment -n dotnet-sample-app-namespace traffic-generator --patch '{"spec": {"template": {"spec": {"containers": [{"name": "e2e-test-resource", "imagePullPolicy": "Always"}]}}}}' + kubectl patch deployment traffic-generator -n dotnet-sample-app-namespace --type='json' -p='[{"op": "add", "path": "/spec/template/spec/imagePullSecrets", "value": [{"name": "ecr-secret"}]}]' + + # Add the appropriate environment variables to the traffic generator + kubectl set env -n dotnet-sample-app-namespace deployment/traffic-generator \ + MAIN_ENDPOINT=$(kubectl get pods -n dotnet-sample-app-namespace --selector=app=dotnet-sample-app -o jsonpath='{.items[0].status.podIP}'):8080 \ + REMOTE_ENDPOINT=$(kubectl get pod -n dotnet-sample-app-namespace --selector=app=dotnet-remote-app -o jsonpath='{.items[0].status.podIP}') \ + ID=${var.test_id} + + sleep 10 + EOF + ] + } +} diff --git a/terraform/dotnet/k8s/deploy/resources/dotnet-frontend-service-depl.yaml b/terraform/dotnet/k8s/deploy/resources/dotnet-frontend-service-depl.yaml new file mode 100644 index 000000000..2e088125d --- /dev/null +++ b/terraform/dotnet/k8s/deploy/resources/dotnet-frontend-service-depl.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dotnet-sample-app-deployment-${TESTING_ID} + namespace: dotnet-sample-app-namespace +spec: + replicas: 1 + selector: + matchLabels: + app: dotnet-sample-app + strategy: {} + template: + metadata: + labels: + app: dotnet-sample-app + annotations: + instrumentation.opentelemetry.io/inject-dotnet: "true" + spec: + containers: + - name: dotnet-frontend + image: ${IMAGE} + ports: + - containerPort: 8080 + env: + - name: LISTEN_ADDRESS + value: "0.0.0.0:8080" + - name: ASPNETCORE_URLS + value: "http://+:8080" + imagePullSecrets: + - name: ecr-secret \ No newline at end of file diff --git a/terraform/dotnet/k8s/deploy/resources/dotnet-remote-service-depl.yaml b/terraform/dotnet/k8s/deploy/resources/dotnet-remote-service-depl.yaml new file mode 100644 index 000000000..6061afdc3 --- /dev/null +++ b/terraform/dotnet/k8s/deploy/resources/dotnet-remote-service-depl.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dotnet-sample-r-app-deployment-${TESTING_ID} + namespace: dotnet-sample-app-namespace +spec: + replicas: 1 + selector: + matchLabels: + app: dotnet-remote-app + strategy: {} + template: + metadata: + labels: + app: dotnet-remote-app + annotations: + instrumentation.opentelemetry.io/inject-dotnet: "true" + spec: + containers: + - name: dotnet-remote + image: ${IMAGE} + env: + - name: LISTEN_ADDRESS + value: "0.0.0.0:8081" + - name: ASPNETCORE_URLS + value: "http://+:8081" + ports: + - containerPort: 8081 + imagePullSecrets: + - name: ecr-secret \ No newline at end of file diff --git a/terraform/dotnet/k8s/deploy/variables.tf b/terraform/dotnet/k8s/deploy/variables.tf new file mode 100644 index 000000000..421a6ce53 --- /dev/null +++ b/terraform/dotnet/k8s/deploy/variables.tf @@ -0,0 +1,49 @@ +# ------------------------------------------------------------------------ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +# ------------------------------------------------------------------------- + +variable "test_id" { + default = "dummy-123" +} + +variable "aws_region" { + default = "" +} + +variable "user" { + default = "ec2-user" +} + +variable "ssh_key" { + default = "" + description = "This variable is responsible for providing the SSH key of the master node to allow terraform to interact with the cluster" +} + +variable "host" { + default = "" + description = "This variable is responsible for defining which host (ec2 instance) we connect to for the K8s-on-EC2 test" +} + +variable "repository" { + default = "aws-application-signals-test-framework" +} + +variable "patch_image_arn" { + default = "" +} + +variable "release_testing_ecr_account" { + default = "" + description = "This variable is to give the k8s cluster ecr secret to pull image from the staging image ecr" +} \ No newline at end of file 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 1d2fdb2b3..dcfe4bac2 100644 --- a/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java +++ b/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java @@ -211,6 +211,24 @@ public enum PredefinedExpectedTemplate implements FileConfig { DOTNET_EC2_ASG_CLIENT_CALL_METRIC("/expected-data-template/dotnet/ec2/asg/client-call-metric.mustache"), DOTNET_EC2_ASG_CLIENT_CALL_TRACE("/expected-data-template/dotnet/ec2/asg/client-call-trace.mustache"), + /** DotNet K8s Test Case Validations */ + DOTNET_K8S_OUTGOING_HTTP_CALL_LOG("/expected-data-template/dotnet/k8s/outgoing-http-call-log.mustache"), + DOTNET_K8S_OUTGOING_HTTP_CALL_METRIC("/expected-data-template/dotnet/k8s/outgoing-http-call-metric.mustache"), + DOTNET_K8S_OUTGOING_HTTP_CALL_TRACE("/expected-data-template/dotnet/k8s/outgoing-http-call-trace.mustache"), + + DOTNET_K8S_AWS_SDK_CALL_LOG("/expected-data-template/dotnet/k8s/aws-sdk-call-log.mustache"), + DOTNET_K8S_AWS_SDK_CALL_METRIC("/expected-data-template/dotnet/k8s/aws-sdk-call-metric.mustache"), + DOTNET_K8S_AWS_SDK_CALL_TRACE("/expected-data-template/dotnet/k8s/aws-sdk-call-trace.mustache"), + + DOTNET_K8S_REMOTE_SERVICE_LOG("/expected-data-template/dotnet/k8s/remote-service-log.mustache"), + DOTNET_K8S_REMOTE_SERVICE_METRIC("/expected-data-template/dotnet/k8s/remote-service-metric.mustache"), + DOTNET_K8S_REMOTE_SERVICE_TRACE("/expected-data-template/dotnet/k8s/remote-service-trace.mustache"), + + DOTNET_K8S_CLIENT_CALL_LOG("/expected-data-template/dotnet/k8s/client-call-log.mustache"), + DOTNET_K8S_CLIENT_CALL_METRIC("/expected-data-template/dotnet/k8s/client-call-metric.mustache"), + DOTNET_K8S_CLIENT_CALL_TRACE("/expected-data-template/dotnet/k8s/client-call-trace.mustache"), + + /** DotNet EKS Linux Test Case Validations */ DOTNET_EKS_LINUX_OUTGOING_HTTP_CALL_LOG("/expected-data-template/dotnet/eks/linux/outgoing-http-call-log.mustache"), DOTNET_EKS_LINUX_OUTGOING_HTTP_CALL_METRIC("/expected-data-template/dotnet/eks/linux/outgoing-http-call-metric.mustache"), diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/aws-sdk-call-log.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/aws-sdk-call-log.mustache new file mode 100644 index 000000000..3870db763 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/aws-sdk-call-log.mustache @@ -0,0 +1,30 @@ +[{ + "EC2.InstanceId": "^i-[A-Za-z0-9]{17}$", + "Environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "K8s.Namespace": "^{{appNamespace}}$", + "K8s.Node": "^i-[A-Za-z0-9]{17}$", + "K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "PlatformType": "^K8s$", + "Service": "^{{serviceName}}$", + "Operation": "GET aws-sdk-call", + "Version": "^1$", + "Telemetry.Source": "^LocalRootSpan$", + "Host": "^ip(-[0-9]{1,3}){4}.*$" +}, +{ + "EC2.InstanceId": "^i-[A-Za-z0-9]{17}$", + "Environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "K8s.Namespace": "^{{appNamespace}}$", + "K8s.Node": "^i-[A-Za-z0-9]{17}$", + "K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "PlatformType": "^K8s$", + "Service": "^{{serviceName}}$", + "Operation": "GET /aws-sdk-call", + "Version": "^1$", + "RemoteService": "AWS::S3", + "RemoteOperation": "GetBucketLocation", + "Telemetry.Source": "^ClientSpan$", + "Host": "^ip(-[0-9]{1,3}){4}.*$" +}] \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/aws-sdk-call-metric.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/aws-sdk-call-metric.mustache new file mode 100644 index 000000000..be2ace17c --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/aws-sdk-call-metric.mustache @@ -0,0 +1,375 @@ +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET aws-sdk-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /aws-sdk-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: AWS::S3 + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /aws-sdk-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + - + name: RemoteResourceType + value: AWS::S3::Bucket + - + name: RemoteResourceIdentifier + value: {{testingId}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + - + name: RemoteResourceType + value: AWS::S3::Bucket + - + name: RemoteResourceIdentifier + value: {{testingId}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET aws-sdk-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /aws-sdk-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: AWS::S3 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /aws-sdk-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + - + name: RemoteResourceType + value: AWS::S3::Bucket + - + name: RemoteResourceIdentifier + value: {{testingId}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + - + name: RemoteResourceType + value: AWS::S3::Bucket + - + name: RemoteResourceIdentifier + value: {{testingId}} + + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET aws-sdk-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /aws-sdk-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: AWS::S3 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /aws-sdk-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + - + name: RemoteResourceType + value: AWS::S3::Bucket + - + name: RemoteResourceIdentifier + value: {{testingId}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GetBucketLocation + - + name: RemoteService + value: AWS::S3 + - + name: RemoteResourceType + value: AWS::S3::Bucket + - + name: RemoteResourceIdentifier + value: {{testingId}} \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/aws-sdk-call-trace.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/aws-sdk-call-trace.mustache new file mode 100644 index 000000000..4c17aefc4 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/aws-sdk-call-trace.mustache @@ -0,0 +1,56 @@ +[{ + "name": "^{{serviceName}}$", + "http": { + "request": { + "url": "^{{endpoint}}/aws-sdk-call$", + "method": "^GET$" + } + }, + "aws": { + "account_id": "^{{accountId}}$" + }, + "annotations": { + "aws.local.service": "^{{serviceName}}$", + "aws.local.operation": "^GET aws-sdk-call$", + "aws.local.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$" + }, + "metadata": { + "default": { + "K8s.Namespace": "^{{appNamespace}}", + "otel.resource.K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "otel.resource.K8s.Node": "^i-[A-Za-z0-9]{17}$", + "otel.resource.K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "otel.resource.host.name": "^ip(-[0-9]{1,3}){4}.*$", + "PlatformType": "^K8s$", + "aws.span.kind": "^LOCAL_ROOT$" + } + }, + "subsegments": [ + { + "name": "^S3$", + "aws": { + "region": "{{region}}", + "operation": "^GetBucketLocation$" + }, + "annotations": { + "aws.local.service": "^{{serviceName}}$", + "aws.local.operation": "^GET /aws-sdk-call$", + "aws.remote.service": "^AWS::S3$", + "aws.remote.operation": "^GetBucketLocation$", + "aws.local.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "aws.remote.resource.type": "AWS::S3::Bucket" + }, + "metadata": { + "default": { + "K8s.Namespace": "^{{appNamespace}}$", + "PlatformType": "^K8s$", + "aws.span.kind": "^CLIENT$" + } + }, + "namespace": "^aws$" + } + ] +}, +{ + "name": "^S3$" +}] \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/client-call-log.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/client-call-log.mustache new file mode 100644 index 000000000..fe0078fa9 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/client-call-log.mustache @@ -0,0 +1,30 @@ +[{ + "EC2.InstanceId": "^i-[A-Za-z0-9]{17}$", + "Environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "K8s.Namespace": "^{{appNamespace}}$", + "K8s.Node": "^i-[A-Za-z0-9]{17}$", + "K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "PlatformType": "^K8s$", + "Service": "^{{serviceName}}$", + "Operation": "InternalOperation", + "Version": "^1$", + "Telemetry.Source": "^LocalRootSpan$", + "Host": "^ip(-[0-9]{1,3}){4}.*$" +}, +{ + "EC2.InstanceId": "^i-[A-Za-z0-9]{17}$", + "Environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "K8s.Namespace": "^{{appNamespace}}$", + "K8s.Node": "^i-[A-Za-z0-9]{17}$", + "K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "PlatformType": "^K8s$", + "Service": "^{{serviceName}}$", + "Operation": "InternalOperation", + "Version": "^1$", + "RemoteService": "local-root-client-call:80", + "RemoteOperation": "GET /", + "Telemetry.Source": "^ClientSpan$", + "Host": "^ip(-[0-9]{1,3}){4}.*$" +}] \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/client-call-metric.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/client-call-metric.mustache new file mode 100644 index 000000000..74b35db3a --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/client-call-metric.mustache @@ -0,0 +1,269 @@ +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: GET client-call + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: InternalOperation + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: InternalOperation + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: local-root-client-call:80 + - + name: RemoteOperation + value: GET / + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: local-root-client-call:80 + - + name: RemoteOperation + value: GET / + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: local-root-client-call:80 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: GET client-call + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: InternalOperation + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: InternalOperation + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: local-root-client-call:80 + - + name: RemoteOperation + value: GET / + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: local-root-client-call:80 + - + name: RemoteOperation + value: GET / + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: local-root-client-call:80 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: GET client-call + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: InternalOperation + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Operation + value: InternalOperation + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: local-root-client-call:80 + - + name: RemoteOperation + value: GET / + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: local-root-client-call:80 + - + name: RemoteOperation + value: GET / + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: local-root-client-call:80 \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/client-call-trace.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/client-call-trace.mustache new file mode 100644 index 000000000..3fae65447 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/client-call-trace.mustache @@ -0,0 +1,58 @@ +[{ + "name": "^{{serviceName}}$", + "annotations": { + "aws.local.service": "^{{serviceName}}$", + "aws.local.operation": "^InternalOperation$", + "aws.local.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$" + }, + "metadata": { + "default": { + "otel.resource.K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "otel.resource.K8s.Node": "^i-[A-Za-z0-9]{17}$", + "otel.resource.K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "otel.resource.host.name": "^ip(-[0-9]{1,3}){4}.*$" + } + }, + "subsegments": [ + { + "name": "^local-root-client-call:80$", + "http": { + "request": { + "url": "^http://local-root-client-call/$", + "method": "^GET$" + } + }, + "annotations": { + "aws.local.service": "^{{serviceName}}$", + "aws.local.operation": "^InternalOperation$", + "aws.remote.service": "^local-root-client-call:80$", + "aws.remote.operation": "GET /", + "aws.local.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$" + }, + "metadata": { + "default": { + "K8s.Namespace": "^{{appNamespace}}$", + "PlatformType": "^K8s$", + "aws.span.kind": "^LOCAL_ROOT$" + } + }, + "namespace": "^remote$" + } + ] +}, +{ + "name": "^local-root-client-call:80$", + "http": { + "request": { + "url": "^http://local-root-client-call/$", + "method": "^GET$" + }, + "response": { + "content_length": 0 + } + }, + "annotations": { + "aws.local.service": "^local-root-client-call:80$", + "aws.local.operation": "^GET /$" + } +}] \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/outgoing-http-call-log.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/outgoing-http-call-log.mustache new file mode 100644 index 000000000..96cf074d2 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/outgoing-http-call-log.mustache @@ -0,0 +1,31 @@ +[{ + "EC2.InstanceId": "^i-[A-Za-z0-9]{17}$", + "Environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "K8s.Namespace": "^{{appNamespace}}$", + "K8s.Node": "^i-[A-Za-z0-9]{17}$", + "K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "PlatformType": "^K8s$", + "Service": "^{{serviceName}}$", + "Operation": "GET outgoing-http-call", + "Version": "^1$", + "Telemetry.Source": "^LocalRootSpan$", + "Host": "^ip(-[0-9]{1,3}){4}.*$" +}, +{ + "EC2.InstanceId": "^i-[A-Za-z0-9]{17}$", + "Environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "K8s.Namespace": "^{{appNamespace}}$", + "K8s.Node": "^i-[A-Za-z0-9]{17}$", + "K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "PlatformType": "^K8s$", + "Service": "^{{serviceName}}$", + "Operation": "GET /outgoing-http-call", + "Version": "^1$", + "RemoteService": "aws.amazon.com:443", + "RemoteOperation": "GET /", + "Telemetry.Source": "^ClientSpan$", + "Host": "^ip(-[0-9]{1,3}){4}.*$" +}] + diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/outgoing-http-call-metric.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/outgoing-http-call-metric.mustache new file mode 100644 index 000000000..83b87f1c1 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/outgoing-http-call-metric.mustache @@ -0,0 +1,227 @@ +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET outgoing-http-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /outgoing-http-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET / + - + name: RemoteService + value: aws.amazon.com:443 + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET / + - + name: RemoteService + value: aws.amazon.com:443 + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: aws.amazon.com:443 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET outgoing-http-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /outgoing-http-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET / + - + name: RemoteService + value: aws.amazon.com:443 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET / + - + name: RemoteService + value: aws.amazon.com:443 + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: aws.amazon.com:443 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET outgoing-http-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /outgoing-http-call + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET / + - + name: RemoteService + value: aws.amazon.com:443 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET / + - + name: RemoteService + value: aws.amazon.com:443 + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: aws.amazon.com:443 \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/outgoing-http-call-trace.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/outgoing-http-call-trace.mustache new file mode 100644 index 000000000..8dfb15916 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/outgoing-http-call-trace.mustache @@ -0,0 +1,57 @@ +[{ + "name": "^{{serviceName}}$", + "http": { + "request": { + "url": "^{{endpoint}}/outgoing-http-call$", + "method": "^GET$" + } + }, + "aws": { + "account_id": "^{{accountId}}$" + }, + "annotations": { + "aws.local.service": "^{{serviceName}}$", + "aws.local.operation": "^GET outgoing-http-call$", + "aws.local.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$" + }, + "metadata": { + "default": { + "K8s.Namespace": "^{{appNamespace}}", + "otel.resource.K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "otel.resource.K8s.Node": "^i-[A-Za-z0-9]{17}$", + "otel.resource.K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "otel.resource.host.name": "^ip(-[0-9]{1,3}){4}.*$", + "PlatformType": "^K8s$", + "aws.span.kind": "^LOCAL_ROOT$" + } + }, + "subsegments": [ + { + "name": "^aws.amazon.com:443$", + "http": { + "request": { + "url": "^https://aws.amazon.com/$", + "method": "^GET$" + } + }, + "annotations": { + "aws.local.service": "^{{serviceName}}$", + "aws.local.operation": "^GET /outgoing-http-call$", + "aws.remote.service": "^aws.amazon.com:443$", + "aws.remote.operation": "^GET /$", + "aws.local.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$" + }, + "metadata": { + "default": { + "K8s.Namespace": "^{{appNamespace}}$", + "PlatformType": "^K8s$", + "aws.span.kind": "^CLIENT$" + } + }, + "namespace": "^remote$" + } + ] +}, +{ + "name": "^aws.amazon.com:443$" +}] diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/remote-service-log.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/remote-service-log.mustache new file mode 100644 index 000000000..6331b0480 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/remote-service-log.mustache @@ -0,0 +1,31 @@ +[{ + "EC2.InstanceId": "^i-[A-Za-z0-9]{17}$", + "Environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "K8s.Namespace": "^{{appNamespace}}$", + "K8s.Node": "^i-[A-Za-z0-9]{17}$", + "K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "PlatformType": "^K8s$", + "Service": "^{{serviceName}}$", + "Operation": "GET remote-service", + "Version": "^1$", + "Telemetry.Source": "^LocalRootSpan$", + "Host": "^ip(-[0-9]{1,3}){4}.*$" +}, +{ + "EC2.InstanceId": "^i-[A-Za-z0-9]{17}$", + "Environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "RemoteEnvironment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "K8s.Namespace": "^{{appNamespace}}$", + "K8s.Node": "^i-[A-Za-z0-9]{17}$", + "K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "PlatformType": "^K8s$", + "Service": "^{{serviceName}}$", + "Operation": "GET /remote-service", + "Version": "^1$", + "RemoteService": "{{remoteServiceDeploymentName}}", + "RemoteOperation": "GET /healthcheck", + "Telemetry.Source": "^ClientSpan$", + "Host": "^ip(-[0-9]{1,3}){4}.*$" +}] \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/remote-service-metric.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/remote-service-metric.mustache new file mode 100644 index 000000000..b26243617 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/remote-service-metric.mustache @@ -0,0 +1,506 @@ +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET remote-service + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /remote-service + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: Operation + value: GET healthcheck + - + name: Service + value: {{remoteServiceDeploymentName}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{remoteServiceDeploymentName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteEnvironment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + - + name: Service + value: {{serviceName}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteEnvironment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + - + name: Service + value: {{serviceName}} + +- + metricName: Latency + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteEnvironment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: Operation + value: GET /remote-service + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + - + name: Service + value: {{serviceName}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET remote-service + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /remote-service + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: Operation + value: GET healthcheck + - + name: Service + value: {{remoteServiceDeploymentName}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{remoteServiceDeploymentName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteEnvironment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + - + name: Service + value: {{serviceName}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteEnvironment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + - + name: Service + value: {{serviceName}} + +- + metricName: Error + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteEnvironment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: Operation + value: GET /remote-service + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + - + name: Service + value: {{serviceName}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET remote-service + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Operation + value: GET /remote-service + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: Operation + value: GET healthcheck + - + name: Service + value: {{remoteServiceDeploymentName}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{remoteServiceDeploymentName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Service + value: {{serviceName}} + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteEnvironment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + - + name: Service + value: {{serviceName}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteEnvironment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + - + name: Service + value: {{serviceName}} + +- + metricName: Fault + namespace: {{metricNamespace}} + dimensions: + - + name: Environment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: RemoteEnvironment + value: k8s:{{platformInfo}}/{{appNamespace}} + - + name: Operation + value: GET /remote-service + - + name: RemoteOperation + value: GET /healthcheck + - + name: RemoteService + value: {{remoteServiceDeploymentName}} + - + name: Service + value: {{serviceName}} \ No newline at end of file diff --git a/validator/src/main/resources/expected-data-template/dotnet/k8s/remote-service-trace.mustache b/validator/src/main/resources/expected-data-template/dotnet/k8s/remote-service-trace.mustache new file mode 100644 index 000000000..add3ad454 --- /dev/null +++ b/validator/src/main/resources/expected-data-template/dotnet/k8s/remote-service-trace.mustache @@ -0,0 +1,79 @@ +[{ + "name": "^{{serviceName}}$", + "http": { + "request": { + "url": "^{{endpoint}}/remote-service$", + "method": "^GET$" + } + }, + "aws": { + "account_id": "^{{accountId}}$" + }, + "annotations": { + "aws.local.service": "^{{serviceName}}$", + "aws.local.operation": "^GET remote-service$", + "aws.local.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$" + }, + "metadata": { + "default": { + "K8s.Namespace": "^{{appNamespace}}", + "otel.resource.K8s.Workload": "^dotnet-sample-app-deployment-{{testingId}}$", + "otel.resource.K8s.Node": "^i-[A-Za-z0-9]{17}$", + "otel.resource.K8s.Pod": "^dotnet-sample-app-deployment-{{testingId}}(-[A-Za-z0-9]*)*$", + "otel.resource.host.name": "^ip(-[0-9]{1,3}){4}.*$", + "PlatformType": "^K8s$", + "aws.span.kind": "^LOCAL_ROOT$" + } + }, + "subsegments": [ + { + "name": "^{{remoteServiceDeploymentName}}$", + "http": { + "request": { + "url": "^http://(([0-9]{1,3}.){3}[0-9]{1,3}):8081/healthcheck$", + "method": "^GET$" + } + }, + "annotations": { + "aws.local.service": "^{{serviceName}}$", + "aws.local.operation": "^GET /remote-service$", + "aws.remote.service": "^{{remoteServiceDeploymentName}}$", + "aws.remote.operation": "^GET /healthcheck$", + "aws.local.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$", + "aws.remote.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$" + }, + "metadata": { + "default": { + "K8s.Namespace": "^{{appNamespace}}$", + "PlatformType": "^K8s$", + "aws.span.kind": "^CLIENT$" + } + }, + "namespace": "^remote$" + } + ] +}, +{ + "name": "^{{remoteServiceDeploymentName}}$", + "http": { + "request": { + "url": "^http://(([0-9]{1,3}.){3}[0-9]{1,3}):8081/healthcheck$", + "method": "^GET$" + } + }, + "annotations": { + "aws.local.service": "^{{remoteServiceDeploymentName}}$", + "aws.local.operation": "^GET healthcheck$", + "aws.local.environment": "^k8s:{{platformInfo}}/{{appNamespace}}$" + }, + "metadata": { + "default": { + "otel.resource.K8s.Workload": "^{{remoteServiceDeploymentName}}$", + "otel.resource.K8s.Node": "^i-[A-Za-z0-9]{17}$", + "otel.resource.K8s.Pod": "^{{remoteServiceDeploymentName}}(-[A-Za-z0-9]*)*$", + "aws.span.kind": "^LOCAL_ROOT$" + } + } +}] + + diff --git a/validator/src/main/resources/validations/dotnet/k8s/log-validation.yml b/validator/src/main/resources/validations/dotnet/k8s/log-validation.yml new file mode 100644 index 000000000..39b700be3 --- /dev/null +++ b/validator/src/main/resources/validations/dotnet/k8s/log-validation.yml @@ -0,0 +1,24 @@ +- + validationType: "cw-log" + httpPath: "outgoing-http-call" + httpMethod: "get" + callingType: "http" + expectedLogStructureTemplate: "DOTNET_K8S_OUTGOING_HTTP_CALL_LOG" +- + validationType: "cw-log" + httpPath: "aws-sdk-call" + httpMethod: "get" + callingType: "http-with-query" + expectedLogStructureTemplate: "DOTNET_K8S_AWS_SDK_CALL_LOG" +- + validationType: "cw-log" + httpPath: "remote-service" + httpMethod: "get" + callingType: "http-with-query" + expectedLogStructureTemplate: "DOTNET_K8S_REMOTE_SERVICE_LOG" +- + validationType: "cw-log" + httpPath: "client-call" + httpMethod: "get" + callingType: "http" + expectedLogStructureTemplate: "DOTNET_K8S_CLIENT_CALL_LOG" \ No newline at end of file diff --git a/validator/src/main/resources/validations/dotnet/k8s/metric-validation.yml b/validator/src/main/resources/validations/dotnet/k8s/metric-validation.yml new file mode 100644 index 000000000..2acc34b15 --- /dev/null +++ b/validator/src/main/resources/validations/dotnet/k8s/metric-validation.yml @@ -0,0 +1,24 @@ +- + validationType: "cw-metric" + httpPath: "outgoing-http-call" + httpMethod: "get" + callingType: "http" + expectedMetricTemplate: "DOTNET_K8S_OUTGOING_HTTP_CALL_METRIC" +- + validationType: "cw-metric" + httpPath: "aws-sdk-call" + httpMethod: "get" + callingType: "http-with-query" + expectedMetricTemplate: "DOTNET_K8S_AWS_SDK_CALL_METRIC" +- + validationType: "cw-metric" + httpPath: "remote-service" + httpMethod: "get" + callingType: "http-with-query" + expectedMetricTemplate: "DOTNET_K8S_REMOTE_SERVICE_METRIC" +- + validationType: "cw-metric" + httpPath: "client-call" + httpMethod: "get" + callingType: "http" + expectedMetricTemplate: "DOTNET_K8S_CLIENT_CALL_METRIC" \ No newline at end of file diff --git a/validator/src/main/resources/validations/dotnet/k8s/trace-validation.yml b/validator/src/main/resources/validations/dotnet/k8s/trace-validation.yml new file mode 100644 index 000000000..53a6b39bb --- /dev/null +++ b/validator/src/main/resources/validations/dotnet/k8s/trace-validation.yml @@ -0,0 +1,24 @@ +- + validationType: "trace" + httpPath: "outgoing-http-call" + httpMethod: "get" + callingType: "http" + expectedTraceTemplate: "DOTNET_K8S_OUTGOING_HTTP_CALL_TRACE" +- + validationType: "trace" + httpPath: "aws-sdk-call" + httpMethod: "get" + callingType: "http-with-query" + expectedTraceTemplate: "DOTNET_K8S_AWS_SDK_CALL_TRACE" +- + validationType: "trace" + httpPath: "remote-service" + httpMethod: "get" + callingType: "http-with-query" + expectedTraceTemplate: "DOTNET_K8S_REMOTE_SERVICE_TRACE" +- + validationType: "trace" + httpPath: "client-call" + httpMethod: "get" + callingType: "http" + expectedTraceTemplate: "DOTNET_K8S_CLIENT_CALL_TRACE" \ No newline at end of file